SOLID 原则:
S:Single Responsibility Principle,缩写 SRP。单一职责原则。
A class or module should have a single reponsibility。一个类或者模块只负责完成一个职责。
判断类的职责是否足够单一:类中的代码行数、函数或者属性是否过多、类依赖的其他类是否过多、私有方法是否过多、是否比较难给类起一个合适的名字、类中是否有大量的方法都是集中操作类中的某几个属性
单一职责原则通过避免设计大而全的类,避免将不相关的功能耦合在一起,来提高类的内聚性。同时,类职责单一,类依赖的和被依赖的其他类也会变少,减少了代码的耦合性,以此来实现代码的高内聚、低耦合。
类拆分得过细,会适得其反,降低内聚性,也会影响代码的可维护性。
O:Open Closed Principle,缩写 OCP。开闭原则。
software entities (modules, classes, functions, etc.) should be open for extension , but closed for modification。软件实体(模块、类、方法等)应该“对扩展开放、对修改关闭”。
开闭原则是解决代码扩展性问题最有效的设计原则。
添加一个新的功能,应该是通过在已有代码基础上扩展代码(新增模块、类、方法、属性等),而非修改已有代码(修改模块、类、方法、属性等)的方式来完成。
开闭原则并不是说完全杜绝修改,而是以最小的修改代码的代价来完成新功能的开发;同样的代码改动,在粗代码粒度下,可能被认定为“修改”;在细代码粒度下,可能又被认定为“扩展”。
如何做到?时刻具备扩展意识、抽象意识、封装意识。
最常用来提高代码扩展性的方法有:多态、依赖注入、基于接口而非实现编程,以及大部分的设计模式(比如,装饰、策略、模板、职责链、状态)。
L:Liskov Substitution Principle,缩写 LSP。里式替换原则。
If S is a subtype of T, then objects of type T may be replaced with objects of type S, without breaking the program。(1986 年由 Barbara Liskov 提出)
Functions that use pointers of references to base classes must be able to use objects of derived classes without knowing it。(1996 年,Robert Martin)
子类对象(object of subtype/derived class)能够替换程序(program)中父类对象(object of base/parent class)出现的任何地方,并且保证原来程序的逻辑行为(behavior)不变及正确性不被破坏。
哪些代码明显违背了 LSP?子类违背父类声明要实现的功能;子类违背父类对输入、输出、异常的约定;子类违背父类注释中所罗列的任何特殊说明
里式替换原则是用来指导,继承关系中子类该如何设计的一个原则。理解里式替换原则,最核心的就是理解“design by contract,按照协议来设计”这几个字。
父类定义了函数的“约定”(或者叫协议),那子类可以改变函数的内部实现逻辑,但不能改变函数原有的“约定”。这里的约定包括:函数声明要实现的功能;对输入、输出、异常的约定;甚至包括注释中所罗列的任何特殊说明。
I:Interface Segregation Principle,缩写 ISP,接口隔离原则。
Clients should not be forced to depend upon interfaces that they do not use. 客户端不应该被强迫依赖它不需要的接口。其中的“客户端”,可以理解为接口的调用者或者使用者。
“接口”理解为一组接口集合,如果部分接口只被部分调用者使用,我们就需要将这部分接口隔离出来,单独给这部分调用者使用,而不强迫其他调用者也依赖这部分不会被用到的接口。
“接口”理解为单个 API 接口或函数,部分调用者只需要函数中的部分功能,那我们就需要把函数拆分成粒度更细的多个函数,让调用者只依赖它需要的那个细粒度函数。
“接口”理解为 OOP 中的接口,也可以理解为面向对象编程语言中的接口语法。那接口的设计要尽量单一,不要让接口的实现类和调用者,依赖不需要的接口函数。
接口隔离原则与单一职责原则的区别:单一职责原则针对的是模块、类、接口的设计;接口隔离原则相对于单一职责原则,一方面更侧重于接口的设计,另一方面它的思考角度也是不同的。接口隔离原则提供了一种判断接口的职责是否单一的标准:通过调用者如何使用接口来间接地判定。如果调用者只使用部分接口或接口的部分功能,那接口的设计就不够职责单一。
D:Dependency Inversion Principle,缩写 DIP。依赖反转原则。
High-level modules shouldn’t depend on low-level modules. Both modules should depend on abstractions. In addition, abstractions shouldn’t depend on details. Details depend on abstractions. 高层模块(high-level modules)不要依赖低层模块(low-level)。高层模块和低层模块应该通过抽象(abstractions)来互相依赖。除此之外,抽象(abstractions)不要依赖具体实现细节(details),具体实现细节(details)依赖抽象(abstractions)。
所谓高层模块和低层模块的划分,简单来说就是,在调用链上,调用者属于高层,被调用者属于低层。
这条原则主要还是用来指导框架层面的设计,高层模块不依赖低层模块,它们共同依赖同一个抽象,抽象不要依赖具体实现细节。
控制反转:是一个比较笼统的设计思想,并不是一种具体的实现方法,一般用来指导框架层面的设计。这里所说的“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程通过框架来控制。流程的控制权从程序员“反转”给了框架。
依赖注入:是一种具体的编码技巧,不通过 new 的方式在类内部创建依赖类的对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类来使用。
依赖注入框架:依赖注入框架提供的扩展点,简单配置一下所有需要的类及其类与类之间依赖关系,就可以实现由框架来自动创建对象、管理对象的生命周期、依赖注入等原本需要程序员来做的事情。
KISS 原则:
查看全文