设计模式之六大原则(简介)
设计模式是一套总结的理论,由Gang of Four总结出的一套可以反复使用的软件开发经验,它们可以提高代码的重用性,增强代码的的可维护性,以及解决一系列的复杂问题。在提出设计模式之前,专家们首先提出了6大设计模式,也就是接下来要详细介绍的内容。
1.单一职责原则(SRP)
单一职责原则(Single Responsibility Principle)是一个备受争议的设计原则。SRP的原话解释是:There should never be more than one reason for a class to change. 意思也就是说通常情况下,应该有且仅有一个原因引起类的变更。
-
单一职责原则具有如下优点:
-
类的复杂性降低,实现什么职责都有清晰明确的定义。
-
可读性提高
-
可维护性提高。
-
变更引起的风险降低。如果一个接口的单一职责做得好,那么一个借口的修改只对相应的实现类有影响,对其他接口无影响,这对系统的扩展性和可维护性都非常有益。
-
单一职责原则的缺点:
-
单一职责原则提出额一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计得是否优良,但“职责”和“变化原因”都是无法量化的,因项目而异,因环境而异。
单一职责原则很难在项目中得到体现,单一职责原则使用与接口、类,同时也适用于方法。即一个方法尽可能只做一件事。
2.里氏替换原则(LSP)
里氏替换原则(Liskov Substitution Principle)有两种定义:
第一种定义,也是最正宗的定义:If for each object o1 of type S there is an object 02 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when 01 is substituted for 02 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 classed without knowing it.(所有引用基类的地方必须能透明地使用其子类对象。)通过俗点说,只要父类能出现的地方,子类就可以出现,而且替换为子类也不会产生任何错误或异常。
里氏替换原则为良好的继承定义了一套规范:
- 子类必须完全实现父类的方法。
- 子类可以有自己特有的属性和方法。
- 覆盖或实现父类的方法时入参(前置条件)可以被放大。
- 覆盖或实现父类的方法时输出结果可以被缩小。
在项目中,采用里氏替换原则时,尽量避免子类的“个性”,因为一旦子类有了“个性”,把子类当做父类使用,子类的“个性”会被抹杀掉。而把子类作为一个业务来使用,则会让代码之间的耦合关系变得扑朔迷离。
3.依赖倒置原则(DIP)
依赖倒置原则(Dependence Inversion Principle) 的定义为:High level modules should not depend on low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.
通俗来说就是:
- 高层模块不应该依赖于低层模块,两者都应该依赖于其抽象。
- 抽象不应该依赖于细节,细节应该依赖于抽象。
依赖倒置原则在Java中的表现就是:模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;接口或抽象类不依赖于实现类,实现类依赖接口或抽象类。一句话概括,就是:面向接口编程。
依赖倒置原则可以减少类间耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。依赖是可以传递的,对象的依赖传递方式(也叫依赖注入 Dependence Injection)有三种:
- 构造函数方式传递依赖对象。
- Setter方法传递依赖对象。
- 接口声明依赖对象,也叫接口注入。
依赖倒置原则的本质就是通过抽象(接口或者抽象类)使各个类或模块的实现彼此独立,不互相影响,实现模块之间的松耦合。如何在项目中使用这个规则呢?只要遵循以下几个规则即可:
- 每个类尽量都有接口或者抽象类,或者抽象类和接口兼具。
- 变量的表面类型尽量是接口或抽象类。
- 任何类都不应该从具体类派生。
- 尽量不要覆写基类的方法。
- 结合里氏替换原则使用。
4.接口隔离原则
定义: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方法。
- 已经被污染的接口,尽量去修改,若修改的风险较大,尽量采用适配器模式进行转化。
- 了解环境,拒绝盲从。一切从实际需求出发。
5.迪米特法则
迪米特法则(Law of Demeter, LoD)也成为最少知识原则(Least Knowledge Principle,LKP),虽然名字不同,但是描述的都是同一个规则:一个类应该对其他对象有最少的了解。通俗来说就是一个类应该对自己需要耦合或者调用的类知道的最少。
使用迪米特法则可以坚持这样一个原则:如果一个方法放在本类中,既不增加类间关系,也不对本类产生负面影响,那就放置在本类中。
迪米特法则的核心思想就是类间解耦,弱耦合,只有弱耦合了以后,类的复用率才会提高,当然,解耦的同时需要注意,解耦也是有限度的。
6.开闭原则
开闭原则的定义如下:Software entities like classes, modules and functions should e open for extension but closed for modifications.即一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。开闭原则可以提高复用性和维护性。
如何使用开闭原则?
- 抽象约束。其包含三层含义:第一,通过接口或抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的public方法;第二,参数类型、引用对象尽量使用接口或抽象类,而不是实现类;第三,抽象层尽量保持稳定,一旦确定即不允许修改。
- 元数据(metadata)控制模块行为。元数据通俗来说就是配置参数。
- 指定项目章程。
- 封装变化。对变化的封装包含两层含义:第一,将相同的变化封装到一个接口或抽象类中;第二,将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。
《注》以上内容总结自秦小波-《设计模式之禅》,仅为个人学习笔记。