SOLID设计原则

SOLID设计原则主要用于解决如何构建可持续性的软件架构。

SRP

SRP全称为Single Responsibility Principle,译为单一职责原则

定义:

There should never be more than one reason for a class to change.

任何一个软件模块都应该有且仅有一个被修改的原因

Clean Architecture中,重新定义如下:

任何一个软件模块都应该只针对某一类行为负责

相比之下,第二种定义更让人容易理解,因为它是站在职责边界的角度来分析职责的划分。

在面向对象的设计中,高内聚、低耦合是评判一个软件设计好坏的标准。

内聚:模块内部元素之间关联的程度。
耦合:模块之间关联的程度。

高内聚低耦合相辅相成,高内聚自然会带来低耦合的效果。

在软件设计层面上,SRP是一种抽象概念,与高内聚、低耦合是同样的目的,都是为了实现模块功能的职责独立。

“模块仅负责某一类行为“ 不就是高内聚的一种表现方式。


如何实现SPR

上面有提到实现SPR的关键在于聚类行为,那么行为如何聚类呢?

利用领域驱动设计的思想找到模块的边界,从而实现行为的归类。


OCP

OCP全称为Open Closed Principle,译为开闭原则

定义:

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

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

Open对应的是扩展Closed对应的是修改

换一个角度来思考OCP,面对需求变化是否需要修改历史代码?

软件设计应该通过扩展来实现变化,而不是通过修改来实现变化。

修改意味着改变原有逻辑,变化可能带来副作用,尤其对于那些年久失修、无人维护的代码来说。

因此,强调通过扩展的方式来拥抱需求变化更为合适,而这里的扩展的手段是抽象化


如何实现OCP

抽象化是实现OCP的关键,具体可以通过面向接口编程面向抽象编程

通过接口和抽象来限定功能范围,具体行为通过具体实现来控制(多态继承)。

模块之间的依赖仅通过接口和抽象来关联,与具体逻辑无关,每当需求发生变更的时候,在不改变接口和抽象的前提下通过增加新的实现来满足需求。


LSP

LSP全称为Liskov Substitution Principle,译为里氏替换原则

定义:

Functions that use pointers or references to base classes must be able to useobjects of derived classes without knowing it.

引用基类的地方都可以透明地使用其子类对象。

LSP的关键在于可替换

在面向对象的编程方式中,可替换可以基于继承来实现。

继承的特点是子类拥有父类所有堆外暴露的功能。

在实际情况下,继承的使用方式:

  • 代码复用,父类实现共享方法,父类方法不可被重写,父类可实例化
  • 代码复用+多态,父类实现共享方法,并提供待子类实现的抽象方法,父类一般为抽象类,不允许实例化

LSP中的可替换也就是多态的一种表现,通过接口抽象类来做模块之间的关联,与模块的具体实现无关。

OCPLSP是相辅相成的,

前者关注扩展来解决模块内部变化的问题,后者关注可替换来解决模块之间逻辑变化的问题。

ISP

ISP全称为Interface Segregation Principle,译为接口隔离原则

定义:

The dependency of one class to another one should depend on the smallest possible interface.

类间的依赖关系应该建立在最小的接口上。

ISP的关键在于功能的划分

ISP的目的在于避免过多的功能暴露给依赖方,也就是解决Fat Interface

SRP的目的在于职责的划分,属于边界划分的范畴,而ISP更倾向于功能聚类,让依赖者不必关心不需要的功能方法。

职责下所涉及的功能如果太多,可以划分为多个功能聚类来简化功能的检索,不仅可以帮忙开发者更好管理功能,也可以帮忙依赖方对接口功能的划分更加清晰。


先满足ISP还是SRP

在软件设计初期需要考虑的是SRP,职责的划分更为重要。

在解决职责问题之后,在功能设计阶段就需要考虑ISP,包括

  • 解决Fat Interface的问题
  • 功能方法聚类,隔离无关功能,让依赖变得更简单

DIP

DIP全称为Dependency Inversion Principle,译为依赖倒置原则

定义:

High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.

高层模块不应该依赖于低层模块,两者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。

当修改接口的时必然会修改实现,但修改实现不需要修改接口。

接口比实现更稳定。

任何具体逻辑的实现及修改和接口抽象无关,依赖方只需要关注接口是否变化,并不需要关注细节本身(这往往是分工开发中需要注意的问题)。

DIP是一种松耦合的设计原则。

DIP是站在抽象的角度上来建立模型,更类似于伪代码,确定整体的设计思路(模块或功能间的耦合关系)。

OCPLSP是具体的实现方式,通过扩展替换来实现不同逻辑细节。