六大设计原则

设计模式的六大原则有:

  • Single Responsibility Principle:单一职责原则
  • Open Closed Principle:开闭原则
  • Liskov Substitution Principle:里氏替换原则
  • Law of Demeter:迪米特法则
  • Interface Segregation Principle:接口隔离原则
  • Dependence Inversion Principle:依赖倒置原则

把这六个原则的首字母联合起来(两个 L 算做一个)就是 SOLID (solid,稳定的),其代表的含义就是这六个原则结合使用的好处:建立稳定、灵活、健壮的设计

参考资料:

一、 单一职责原则

There should never be more than one reason for a class to change. 一个类应该只有一个发生变化的原因

1. 概念

单一职责原则(Single Responsibility Principle, SRP ),就是一个类只负责一个职责,降低类的复杂度,维护起来更加容易。 这个原则不仅仅局限于类,对于接口和方法也同样适用。通常,接口和方法的单一职责更容易实现。

2. 特点

  • 代码的粒度降低了,类的复杂度降低了。
  • 可读性提高了,每个类的职责都很明确,可读性自然更好。
  • 可维护性提高了,可读性提高了,一旦出现 bug ,自然更容易找到他问题所在。
  • 改动代码所消耗的资源降低了,更改的风险也降低了。

二、 开闭原则

Software entities like classes, modules and functions should be open for extension but closed for modification 一个软件实体,如类、模块和函数应该对扩展开放,对修改关闭

1. 概念

开闭原则(Open Closed Principle, OCP)旨在如何建立稳定灵活的系统,只定义了对修改关闭,对扩展开放。

对软件原有代码进行修改,可能会给旧代码引入错误,也有可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现。

2. 优点

用抽象构建架构,用实现扩展细节。因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保证架构的稳定。而软件中易变的细节,我们用从抽象派生的实现类来进行扩展,当软件需要发生变化时,我们只需要根据需求重新派生一个实现类来扩展就可以了,当然前提是抽象要合理,要对需求的变更有前瞻性和预见性。

三、 里氏替换原则

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

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

1. 概念

里氏替换原则(Liskov Substitution Principle, LSP )是指所有基类在的地方,都可以换成子类,程序还可以正常运行。 这个原则是与面向对象语言的继承特性密切相关,是为了弥补继承的缺陷。

在学习java类的继承时,我们知道继承有一些优点

  • 子类拥有父类的所有方法和属性,从而可以减少创建类的工作量。
  • 提高了代码的重用性。
  • 提高了代码的扩展性,子类不但拥有了父类的所有功能,还可以添加自己的功能。

但有优点也同样存在缺点

  • 继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法。
  • 降低了代码的灵活性。因为继承时,父类会对子类有一种约束。
  • 增强了耦合性。当需要对父类的代码进行修改时,必须考虑到对子类产生的影响。

2. 特点

里氏替换原则对继承进行了规则上的约束,这种约束主要体现在四个方面:

  • 子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。
  • 子类中可以增加自己特有的方法。
  • 当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比- 父类方法的输入参数更宽松。(即只能重载不能重写)
  • 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

四、 迪米特法则

Talk only to your immediate friends and not to strangers

其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。

1. 概念

迪米特法则(Law of Demeter, LOD)要求限制软件实体之间通信的宽度和深度。

过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。它调以下两点:

  • 从依赖者的角度来说,只依赖应该依赖的对象。
  • 从被依赖者的角度说,只暴露应该暴露的方法。

2. 优点

正确使用迪米特法则将有以下两个优点。

  • 降低了类之间的耦合度,提高了模块的相对独立性。
  • 由于亲合度降低,从而提高了类的可复用率和系统的扩展性。

五、 接口隔离原则

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.

注:该原则中的接口,是一个泛泛而言的接口,不仅仅指Java中的接口,还包括其中的抽象类。

1. 概念

接口隔离原则(Interface Segregation Principle, ISP)的定义:

  1. 客户端不应该依赖它不需要的接口。
  2. 类间的依赖关系应该建立在最小的接口上。

2. 优点

接口隔离原则是为了约束接口、降低类对接口的依赖性,遵循接口隔离原则有以下 5 个优点。

  • 将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
  • 接口隔离 提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
  • 如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。
  • 使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。
  • 在项目工程中减少代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。

3. 实现方法

在具体应用接口隔离原则时,应该根据以下几个规则来衡量。

  • 根据接口隔离原则拆分接口时,首先必须满足单一职责原则。
  • 接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑。
  • 为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。
  • 了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同深入了解业务逻辑。
  • 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。

4. 接口隔离原则和单一职责的区别

接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:

  接口隔离原则 单一职责
注重 职责 对接口依赖的隔离
主要 约束类 约束接口
针对 程序中的实现和细节 抽象和程序整体框架的构建

六、 依赖倒置原则

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.

1. 概念

依赖倒置原则(Dependence Inversion Principle)

  1. 上层模块不应该依赖底层模块,它们都应该依赖于抽象。
  2. 抽象不应该依赖于细节,细节应该依赖于抽象。