Giter Site home page Giter Site logo

blog's People

Contributors

okoala avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

blog's Issues

[WIP] S.O.L.I.D 在 TypeScript 中的应用

背景

SOLID 是由 Robert C. Martin,twitter账号:@UncleBob 提出的 面向对象编程面向对象设计 的五个基本原则。刚开始 Uncle Bob 提出时候并没有使用 SOLID 这个名称,直到 Michael Feathers 出现。SOLID 被典型的应用在测试驱动开发上,并且是敏捷开发以及自适应软件开发的基本原则的重要组成部分。

结论

这5大原则对于开发者来说是非常重要的,是大家必须要掌握的知识点。明白和应用这些原则会促使你写出更高质量的代码,进而成为一位更优秀的开发者

应用它对代码来说至少有三个好处:

  • 易维护
  • 易扩展
  • 更健壮

前言

去构建一个好的应用,我们必须去掌握两点:低耦合高内聚

那什么是耦合?

那什么是内聚?

而 **SOLID ** 原则可以帮助我们去完成这个任务。使我们的应用更健壮、更具

概述

缩写 名称 概念
SRP 单一责任原则 一个对象应该仅具有一种单一功能的概念
OCP 开放封闭原则 实体应该是对于扩展开放,对于修改封闭的
LSP 里氏替换原则 对象应该是可以在不改变程序正确性的前提下被它的子类所替换
ISP 接口分离原则 多个特定的接口要好于一个宽泛用途的接口
DIP 依赖倒置原则 一个方法应该依赖于抽象而不是实例

单一责任原则 Single Responsibility Principle

一个类只有一个会导致其变更的原因

意味着一个类有且仅有一个职责。否则,假如一个类超过了一个职责,那将会是我们的代码耦合性更高,在变更的时候就会更容易出错。

好处:
  • 降低耦合度
  • 代码更易懂和更好维护
错误的使用
class Customer {

  private name: String;

  // 各种getter 和 setter方法

  // 这是一个职责
  public storeCustomer(customerName: String): void {
    // 保存customer到数据库
  }

  // 这是另一个职责
  public generateCustomerReport(customerName: String): void {
    // 生成报告
  }
}

storeCustomer 有一个存储customer到数据库的职责,它是一个持久化的职责。所以我们需要把它移出Customer类

generateCustomerReport 有一个生成报表的职责,所以我们也需要把它移出Customer类

正确的使用

我们需要给不同的职责创建不同的类

  • Customer 类:
class Customer {
  
  private name: String;

  // 各种 getter 和 setter
}
  • CustomerDB 类,提供持久化
class CustomerDB {

  public storeCustomer(customerName: String): void { 
    // 存储用户数据进数据库中
  }
  
}
  • CustomerReportGenerator 类,提供报表生成
class CustomerReportGenerator {

  public generateReport(customerName: String): void { 
    // 生成报告
  }
  
}
小结

实践 SIP,让每个类都只拥有单一职责,让我们的代码拥有低耦合高内聚

开放封闭原则 Open Closed Principle

一个软件实体(类, 模块,函数)应该对扩展开发,对修改关闭

依据这个原则,一个软件实体必须是易于扩展新特性的,且不会改变已经存在的代码

对扩展开放: 可以添加新的行为去满足新的需求
对修改关闭: 去扩展新的行为不能去修改已经存在的代码

如果我们应用了这原则,我们会获得一个具有扩展性的系统,而且即使需求改变了,我们的系统也不容易错误。我们可以使用 抽象多态 来应用它

好处
  • 代码具有维护性和重用性
  • 代码更健壮
错误的使用
  • 我们有个 Rectangle 类
class Rectange {

  private width: Number;
  private height: Number;

  // 一堆 getter 和 setter 方法
}
  • 我们还有个 Square 类
class Square {

  private side: Number;

  // 一堆 getter 和 setter 方法
}
  • 我们有个 ShapePrinter 类,用来画这几类图形的
class ShapePrinter {

  public drawShape(shape: Object) {
    if (shape instanceof Rectangle) {
      // Draw Rectangle
    } else if (shape instanceof Square) {
      // Draw Square
    }
  }

}

我们发现,每次要添加新的图形都要修改drawShape方法,所以 ShapePrinter 类并没有对修改关闭

正确的使用
interface Shape {

  draw(): void;

}

class Renctange implements Shape {

  private width: Number;
  private height: Number;

  // getter setter

  public draw(): void {
    // D
  }

}

class Square implements Shape {

  private side: Number;

  public draw(): void {
    // d
  }

}

class ShapePrinter {

  public drawShape(shape: Shape) {
    shape.draw();
  }

}

之后我们要添加一个新的图形,只要实现 Shape 就可以了,ShapePrinter 类完全不需要动。

小结

可以这么说,好的软件架构一定遵循开闭原则的,我们在应用23种设计模式的时候其实目的也是遵循开闭原则。所以好的抽象对保持软件架构的稳定至关重要,软件细节用实现类进行扩展。当软件发生改变的时候我们只需要重新派生出一个实现类就可以了。总的来说就是 用抽象构建框架,用实现扩展细节

里氏替换原则 Liskov Substitution Principle

将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常

通过应用此原则,我们可以验证我们的抽象是否是正确的

好处
  • 代码重用性更高
  • 类层次结构容易理解。
错误的使用
  • 先创建一个 Rectangle 的类
class Rectangle {
  
  private width: Number;
  private height: Number;

  public setWidth(width: Number): void {
     this.width = width;
  }

  public setHeight(height: Number): void {
     this.height= height;
  }

  public getArea(): Number {
    return <number>this.width * <number>this.height;
  }

}
  • 再来创建一个 Square 的类

按照数学上来说,正方形也是长方形中的一种,所以我们把 Square 设为 Rectangle 的子类

class Square extends Rectangle {

  public setWidth(width: Number) {
    super.setWidth(width);
    super.setHeight(width);
  }

  public setHeight(height: Number) {
    super.setWidth(height);
    super.setHeight(height);
  }

}

看这代码,我们发现一个问题,重写 Rectangle 基类之后,setWidth,setHeight本身的行为被改变了。

const rectangle: Rectangle = new Square();
rectangle.setWidth(2);
rectangle.setHeight(5);

// 25

在实际编程中,我们常常会通过重写父类的方法来完成新功能,这样写虽然简单,但是整个继承体系的可复用性会很差。如果还运用多态,那出错的概率就很大了。

正确的使用

一般我们是采用 Abstract Class 或者 Interface 的做法来解决这个问题。
根据这里的情况,我们使用 Interface

interface Shape {
  area(): Number;
}

class Rectangle implements Shape {

  private width: Number;
  private height: Number;

  public setWidth(width: Number): void {
    this.width = width;
  }

  public setHeight(height: Number): void {
    this.height = height;
  }

  public area(): Number {
    return <number>this.width * <number>this.height;
  }
 
}

class Square implements Shape {

  private size: Number;

  public setSize(size: Number): void {
    this.size = size;
  }

  public area() {
    return <number>this.size * <number>this.size;
  }

}

当然,除此之外还有其他的方案。

小结

子类可以扩展父类的功能,但不能改变父类原有的功能

接口分离原则 Interface Segregation Principle

一个类不应该依赖它不需要的接口,应该是建立在最小的接口上

所以实现具体的接口比实现通用的接口更好。

好处
  • 解耦系统
  • 代码容易重构
错误的使用
  • 我们有个 Car 接口

依赖倒置原则 Dependency Inversion Principle

参考资料

SOLID (面向对象设计)
SOLID Development Principles – In Motivational Pictures
SOLID principles using Typescript
SOLID Principles : The Definitive Guide
设计模式六大原则(6):开闭原则

[WIP] 深度分析React Fiber

  • 在了解React Fiber之前,需要先了解5大概念,在不吃透这些概念的情况下,接下来会很难理解要讲的东西:
    • Fiber
    • Call Stack
    • Coroutine
    • Continuation
    • Algebraic Effects

  • 为什么React要重新设计调度算法

传统React的栈情况:

  1. 同步的
  2. 递归的
  3. 渲染和调和

问题存在着:

  1. 比较重的context
  2. 无法拆分成多个chunks
  3. Java-like 的 OO 结构
  • Fiber做了哪些工作
  1. 完整的核心重写
  2. 异步的调和
  3. 原子提交
  • Fiber的目标
  1. 暂停一个事务,并在不久后再重新开始执行
  2. 设定不同类型事务的优先级。
  3. 复用已完成的事务。
  4. 当不再需要时,取消该事务。
  • Fiber的内部
  1. 使用requestAnimationFrame, requestIdleCallback
  2. 使用迭代而非递归
  3. 单链表组件
  4. 用循环来协调和更新
  • Fiber的优点
  1. 不会丢帧
  2. 每一帧都分开事务
  3. 事务完成时进行提交
  4. 可以取消事务优先级
  • Fiber的缺点
  1. 调试困难(React的堆栈信息本身就是反人类的)
  2. 需要更好的工具
  3. 很难去了解问题原因
  4. 非及时更新
  • Fiber的注意
    由于fiber新的调度系统,setState都回是异步的了,所以下面的代码会出大事:
// 错误的
this.setState({
    counter: this.state.counter + this.props.increment
});

正确的姿势:

this.setState((prevState, props) => ({
    counter: prevState.counter + props.increment
}));
  • Fiber的进展
  1. 代码已经在主干了
  2. 将会在React 16 默认打开
  3. http://isfiberreadyyet.com
  • 源码分析
  • 未来展望
  • 总结

[置顶] 我的学习

读书计划

有趣的视频

一些不错的文章

Clean Code JavaScript
从达标到卓越 —— API 设计之道
Animated Intro RxJS

[学习] 线性代数

线性代数为我们提供了一套便捷的概念和语言来讨论 “空间”。

对于大部分的东西来说,如果将其放大,那么几乎都是平直的,所以只要小范围内进行考察,通过用 “平直的东西” 来进行近似,也是可以得到很有用的结果的。平面也是类似。

我们几乎处处都会遇到要处理由多个数值组成的成组的数据的情况。对于这样的数据,我们不是简单的将其看成一组组的数,而是看作 “空间的点” 来进行直观的处理。

名称 字面意思 实际意思
向量 排成一列的数字 有向线段、空间内的点
矩阵 排成矩阵的数字 空间到空间的映射
行列式 麻烦的计算 上面的映射对应的 “体积扩大率”

矩阵满足结合律,但是不满足交换律

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.