要构建一个好的软件系统,应该从写整洁代码开始做起。毕竟,如果建筑所用的砖头质量不佳,那么架构所能起到的作用也非常有限。反之亦然,如果建筑的架构设计不佳,那么其所用的砖头质量再好也没有用。这就是SOLID设计原则要解决的问题。
SOLID设计原则的历史悠久,是在20世纪80年代末期产生的,是由下面5个原则的首字母组成:
SRP:单一职责原则
OCP:开闭原则
LSP:里氏替换原则
ISP:接口隔离原则
DIP:依赖反转原则
SRP:单一职责原则
一个软件系统的最佳结构高度依赖于开发这个系统的组织的内部结构。这样,每个软件模块都有且只有一个需要被改变的理由。
SRP还有以下几种描述:
“任何一个软件模块都应该有且仅有一个被修改的原因。”
“任何一个软件模块都应该只对一个用户或系统利益相关者负责。”
“任何一个软件模块都应该只对某一类行为者负责。”
单一职责原则主要讨论的是函数和类之间的关系 -- 但是它在两个讨论层面上会以不同的形式出现。在组件层面,我们可以将其称为共同闭包原则(Common Closure Principle),在软件架构层面,它则是用于奠定架构边界的变更轴心。
OCP:开闭原则
如果软件系统想要更容易被改变,那么其设计就必须允许新增代码来修改系统行为,而非只能靠修改原来的代码。
“设计良好的计算机软件应该易于扩展,同时抗拒修改。”换句话说,就是一个设计良好的计算机系统应该在不需要修改的前提下就能很容易被扩展。
OCP是我们进行系统架构设计的主导原则,其主要目标是让系统易于扩展,同时限制其每次被修改所影响的范围。实现方式是通过将系统划分为一系列组件,并且将这些组件间的依赖关系按层次进行组织,使得高阶组件不会因低阶组件被修改而受到影响。
LSP:里氏替换原则
如果想用可替换的组件来构建软件系统,那么这些组件就必须遵守同一个约定,以便让这些组件可以相互替换。这个约定就叫做里氏替换原则。
如果对于每个类型是S的对象o1都存在一个类型为T的对象o2,能使操作T类型的程序P在用o2替换o1时行为保持不变,我们就可以将S称为T的子类型。
注意一个特例:正方形并不是长方形的子类型,因为长方形的高和宽可以分别修改,而正方形的高和宽则必须一同修改。
LSP刚开始只不过是指导如何使用继承关系的一种方法,随着时间的推移,LSP组件演变成一种更广泛的、用于指导接口与其实现方式的设计原则。
LSP可以并且应该被应用于软件架构层面,因为一旦违背了可替换性,该系统架构就不得不为此增添大量复杂的应对机制。
ISP:接口隔离原则
这个设计原则主要告诫软件设计师应该在设计中避免不必要的依赖。
任何层次的软件设计如果依赖了不需要的东西,都会是有害的。从源代码层次来说,这样的依赖关系会导致不必要的重新编译和重新部署,对更高层次的软件架构设计来说,问题也是一样的。
DIP:依赖反转原则
该设计原则指出高层策略性代码不应该依赖实现底层细节的代码,相反,哪些实现底层细节的代码应该依赖高层策略性的代码。
DIP主要想告诉我们的是,如果想要设计一个灵活的系统,在源代码层次的依赖关系中就应该多饮用抽象类型,而不是具体实现。DIP可以归纳为下面几条具体的编码守则:
应在代码中多使用抽象接口,尽量避免使用哪些多变的具体实现类
不要在具体实现类上创建衍生类
不要覆盖(override)包含任何具体实现的函数
应该避免在代码中写入与任何具体实现相关的名字,或者是其他容易变动的事物的名字
总结的话
SOLID原则的主要作用是告诉我们如何将数据和函数组织成类,以及如何将这些类链接起来称为程序。这里讨论的设计原则并不仅仅适用于面向对象编程。这里的类仅仅代表了一种数据和函数的分组,每个软件系统都会有自己的分类系统,不管它们各自是不是将其称为“类”,事实上都是SOLID原则的使用领域。
更多相关阅读:
- 当架构师们谈“设计”,他们是在说什么?
LRU算法在生活中的应用
程序员必须掌握的核心算法有哪些?
技术要活到老,学到老
为何一定要学习机器学习?