设计原则

SOLID+DRY+KISS+YAGNI+LOD

Posted by songjunhao on November 23, 2022

软件设计原则

软件开发中有各种设计原则,SOLID原则,DRY原则,KISS原则,YAGNI原则,LOD法则。这些原则最终目的都是为了提高软件质量,只是侧重点和看问题的角度各有不同。

设计原则总结思维导图.jpg

SOLID 原则

SOLID 原则是一套比较成体系的设计原则。它不仅可以指导我们设计模块(在面向对象领域,模块主要指的就是类),还可以被当作一把尺子,来衡量我们设计的有效性。

SOLID 原则是五个设计原则首字母的缩写,它们分别是:

  • 单一职责原则(Single responsibility principle,SRP)
  • 开放封闭原则(Open–closed principle,OCP)
  • Liskov 替换原则(Liskov substitution principle,LSP)
  • 接口隔离原则(Interface segregation principle,ISP)
  • 依赖倒置原则(Dependency inversion principle,DIP)

SRP单一职责原则

定义

《敏捷软件开发:原则、实践与模式》中,定义为 一个模块应该有且仅有一个变化的原因

《架构整洁之道》中,定义为“一个模块应该对一类且仅对一类行为者(actor)负责”。

理解

单一职责原则不是说一个类只干一件事,而是说只有一个原因会去改变这个类这个模块,去让我们去不得不修改这个类这个模块。关键在于 变化的原因

变化的来源在于需求的变更,需求的变更在于业务的调整,所以要单一职责原则也要保证软件面对的用户也是同一类才是单一职责,因为可能有的变化仅针对部分用户,比如订单对于客户和仓储管理人员是不同的,可能有的仓储的功能影响到订单但用户是不需要这部分变化,那么就可能出现两种变化来源,将两者分离才可以满足的那一刻职责原则。

什么样的类可能不满足单一职责原则:

  • 类中的代码行数、函数或者属性过多
  • 类依赖的其他类过多或者依赖类的其他类过多
  • 私有方法过多
  • 比较难给类起一个合适的名字
  • 类中大量的方法都是集中操作类中的某几个属性

意义

单一职责原则可以提高代码高内聚、低耦合,提高代码的复用性、可读性、可维护性。

OCP开闭原则

定义

开闭原则是说

软件实体(类、模块、函数)应该对扩展开放,对修改封闭。

理解

想要实现开闭原则关键在于预留扩展点。

  1. 寻找扩展点需要分离关注点,然后寻找共性。
  2. 将其共性抽象为更高层的接口。
  3. 面向接口编程的方式,运用多态技术具体实现开闭原则。

意义

开闭原则的优点是降低修改代码带来的影响,通过预留扩展点的方式提高代码的可扩展性可维护性。 对拓展开放可以应对变化,对修改关闭可以保证已有代码的稳定性,最终可以让系统更有弹性。

LSP里式替换原则

定义

里式替换原则是说

若每个类型 S 的对象 o1,都存在一个类型 T 的对象 o2,使得在所有针对 T 编程的程序 P 中,用 o1 替换 o2 后,程序 P 行为保持不变,则 S 是 T 的子类型。

简单点说就是 子类型可以任意替换父类型。

理解

关键是站在父类角度思考子类实现。

继承需要满足 IS-A 的关系。但 IS-A 的关键在于行为上的一致性,而不是意义上的一致。

其实也就是说在使用继承的时候,我们应该考虑父类对于这个行为的期待是什么?这个行为真正的目标是做什么?而不是站在子类的角度去思考这个行为应该如何处理。

意义

违反里氏替换原则也会降低可测试性。里氏替换原则更多的像是一种协议,在不违反这个协议的前提下,系统可以提升可读性,可维护性,可扩展性,可测试性。

ISP接口隔离原则

定义

接口隔离原则是说

不应强迫使用者依赖于它们不用的方法。

理解

理解这个原则需要站在使用者的角度。

最常见就是接口方法过多,导致使用该接口的类依赖了不必要的方法。

所以在设计接口方法的时候,我们应该站在使用者的角度去思考使用者应该依赖多少方法。是不是过多了。

意义

依赖不必要的方法会使使用者迷惑,降低代码的可读性,可维护性。反之只依赖必要的方法,会提升可读性,可维护性。

DIP依赖倒置原则

定义

依赖倒置原则是说

高层模块不应依赖于低层模块,二者应依赖于抽象。 抽象不应依赖于细节,细节应依赖于抽象。

理解

依赖导致原则是目前比较常见的原则,Spring实现的依赖注入是依赖倒置原则的最好体现。

类,函数不应该依赖于实现,而应该依赖于抽象,比如接口。即使目前可能只有一种实现,比方说缓存使用redis,也应该抽象出一层Cache接口,依赖于Cache而不直接依赖于Redis。

  • 任何变量都不应该声明为一个具体类。
  • 任何类都不应继承自具体类。
  • 任何方法都不应该改写父类中已经实现的方法。

关键在于判断是否直接依赖于实现,还可以通过代码是否可测试,可隔离依赖来判断是否满足依赖导致原则。

意义

依赖导致原则可以解耦依赖,提升代码的可测试性。提升代码的内聚性,降低耦合性。

其他原则

DRY 原则

定义

不要重复自己

理解

就是不要编写重复代码。

可能出现重复的地方如下

  • 实现逻辑重复
  • 功能语义重复
  • 代码执行重复

提高代码复用性的一些手段,包括:解依赖、单一职责原则、业务与非业务逻辑分离(关注点分离)、通用代码提取函数或者类、继承、多态、抽象、封装、应用模板等设计模式。

意义

主要是提高代码复用性,防止重构中的”霰弹式修改坏味道”。也就是说大量的重复导致一个功能的修改需要修改散落在各种类各个地方的代码。

KISS 原则

定义

尽量保持简单

理解

简单不是以代码行数考量的。

对于如何写出满足 KISS 原则的代码,可参考下面几条指导原则:

  • 不要使用同事可能不懂的技术来实现代码。
  • 不要重复造轮子,善于使用已经有的工具类库。
  • 不要过度优化。

意义

提升可读性,可维护性。

YAGNI 原则

定义

You Ain’t Gonna Need It。 你不会需要它

理解

从要不要做的角度去思考

  1. 不要去设计当前用不到的功能。
  2. 不要去编写当前用不到的代码。
  3. 核心思想就是:不要做过度设计。

意义

防止过度设计。

LOD法则

定义

不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口。

理解

站在使用者的角度看只依赖自己需要的类,属性,接口,方法。

站在提供者的角度看自己的变化对使用者的影响。

有依赖关系的类之间,尽量只依赖必要的接口。迪米特法则是希望减少类之间的耦合,让类越独立越好。每个类都应该少了解系统的其他部分。

意义

如果提供者发生变化,使用者会比较少,系统就更安全。或者说如果有改动,需要改动的类就更少。