一、设计模式的六大原则

1、单一职责原则(Single Responsibility Principle)

Single Responsibility Principle, SRP 的定义:

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

单一职责原则要求一个接口或类只有一个原因引起变化

2、里氏替换原则(Liskov Substitution Principle)

Liskov Substitution Principle, LSP 的定义:

第一种定义:If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.

如果对每一个类型为 S 的对象 o1,都有类型为 T 的对象 o2,使得以 T 定义的所有程序 P 在所在的对象 o1 都代换为 o2 时,程序 P 的行为没有发生变化,那么类型 S 是类型 T 的子类型。

第二种定义:Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

所有引用基类的地方必须能透明地使用其子类的对象。

3、依赖倒转原则(Dependence Inversion Principle)

Dependence Inversion Principle, DIP 的定义:

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.

翻译过来,有三重含义:

  • 高层模块不应该依赖底层模块,两者都应该依赖其抽象。
  • 抽象不应该依赖细节。
  • 细节应该依赖抽象。

依赖倒置原则在 Java 语言中的表现就是:

  • 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。
  • 接口或抽象类不依赖于实现类。
  • 实现类依赖与接口或抽象类。

采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并发开发引起的风险,提高代码的可读性和可维护性。

最佳实践:

  • 每个类尽量都有接口或抽象类,或者两者都具备。接口负责定义 public 属性和方法,并且声明与其他对象的依赖关系,抽象类负责公共构造部分的实现,实现类准确的实现业务逻辑,同时在适当的世界对父类进行细化。
  • 变量的表面类型尽量是接口或者是抽象类。
  • 任何类都不应该从具体类派生。
  • 尽量不要覆写(Override)基类的方法。
  • 结合里氏替换原则使用。

4、接口隔离原则(Interface Segregation Principle)

Interface Segregation Principle, LSP 的定义

CLients Should not be forced to depend upon interfaces that they don’t use.

客户端不应该依赖它不需要的接口。

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

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

保证接口的纯洁性:

  • 接口要尽量小

这是接口隔离原则的核心定义。但是 “小” 是有限度的,首先就是不能违反单一职责原则,已经做到单一职责的接口不应该再分。即,根据接口隔离原则拆分接口时,首先必须满足单一职责原则。

  • 接口要高内聚

高内聚就是提高接口、类、模块的处理能力,减少对外的交互。具体到接口隔离原则就是,要求在接口中尽量少公布 public 方法,接口是对外的承诺,承诺越少对系统的开发越有利,变更的风险也就越少,同时也有利于降低成本。

  • 定制服务

定制服务就是单独为一个个体提供优良的服务。要求就是:只提供访问者需要的方法。

  • 接口设计是有限度的

接口的设计粒度越小,系统越灵活。但是,灵活的同时也带来了结构的复杂化,开发难度增加,可维护性降低。所以接口设计一定要注意适度。

最佳实践:

  • 一个接口只服务于一个子模块或业务逻辑;
  • 通过业务逻辑研所接口中的 public 方法;
  • 已经被污染了的接口,尽量去修改,若更改的风险较大,则采用适配器模式进行转化处理;
  • 了解环境,拒绝盲从,深入了解业务逻辑才能设计出好接口。

5、迪米特法则(最少知识原则)(Demeter Principle)

Least Knowledge Principle,LKP 的定义:

Only talk to your immedate friends(只与直接的朋友通信。)
朋友之间也是有距离的
是自己的就是自己的
谨慎使用 Serializable

最佳实践:

类间解耦,弱耦合。但其要求的结果就是产生大量的中转或跳转类,导致系统的复杂性提高,同时也为维护带来了难度。如果一个类跳转两次以上才访问到另一个类,就需要想办法进行重构了。

6、合成复用原则(Composite Reuse Principle)

Software entities like classes, modules and functions should be open for extension but closed for modifications.

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

开闭原则是最基本的原则,是其他原则和设计模式的精神。

开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类等,后面的具体设计中我们会提到这点。

最佳实践:

  • 抽象约束

抽象是对一组食物的通用描述,没有具体的实现,也就可以跟随需求的变化而变化。

第一,通过接口或抽象类约束扩展,不允许出现在接口或抽象类中不存在 public 方法;

第二,参数类型、引用对象尽量使用接口或者抽象类,而不是实现类;

第三,抽象层尽量保持稳定,一旦确定即不允许修改。

  • 元数据(metadata)控制模块行为

尽量使用元数据来控制程序的行为,减少重复开发。

元数据,就是用来描述环境和数据的数据,通俗地说就是配置参数,参数可以从文件获得,也可以从数据库中获得。

  • 制定项目章程

章程中指定了所有人员都必须遵守的约定,对项目来说,约定优于配置。

  • 封装变化

第一,将相同的变化封装到一个接口或抽象类中;

第二,将不同的变化封装在不同的接口或抽象类中。