《设计模式之禅》之——六大设计原则解读

Posted zjfjava

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《设计模式之禅》之——六大设计原则解读相关的知识,希望对你有一定的参考价值。

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

1. 单一职责原则定义

应该有且仅有一个原因引起类的变更。

2. 单一职责原则好处

  • 类的复杂性降低,实现什么职责都有清晰明确的定义;
  • 可读性提高,复杂性降低,那当然可读性提高了;
  • 可维护性提高,可读性提高,那当然更容易维护了;
  • 变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助。

3. 单一职责适用于接口、类,同时也适用于方法

对于接口,我们在设计的时候一定要做到单一,但是对于实现类就需要多方面考虑了。生搬硬套单一职责原则会引起类的剧增,给维护带来非常多的麻烦,而且过分细分类的职责也会人为地增加系统的复杂性。本来一个类可以实现的行为硬要拆成两个类,然后再使用聚合或组合的方式耦合在一起,人为制造了系统的复杂性。所以原则是死的,人是活的,这句话很有道理。

类的单一职责确实受非常多因素的制约,纯理论地来讲,这个原则是非常优秀的,但是现实有现实的难处,你必须去考虑项目工期、成本、人员技术水平、硬件情况、网络情况甚至有时候还要考虑政府政策、垄断协议等因素。

对于单一职责原则,我的建议是接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化。

二、里氏替换原则(Liskov Substitution Principle,LSP)

引入里氏替换原则来减少面向对象的语言 “继承” 的缺点

1. 继承的缺点

继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法;

降低代码的灵活性。子类必须拥有父类的属性和方法,让子类自由的世界中多了些约束;

增强了耦合性。当父类的常量、变量和方法被修改时,必需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能带来非常糟糕的结果——大片的代码需要重构。

2. 里氏替换原则定义

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

2.1 包含四层含义

  • 子类必须完全实现父类的方法
  • 子类可以有自己的个性
  • 覆盖或实现父类的方法时输入参数可以被放大
  • 覆写或实现父类的方法时输出结果可以被缩小

采用里氏替换原则的目的就是增强程序的健壮性,版本升级时也可以保持非常好的兼容性。即使增加子类,原有的子类还可以继续运行。

3. 项目应用

在实际项目中,每个子类对应不同的业务含义,使用父类作为参数,传递不同的子类完成不同的业务逻辑。

在项目中,采用里氏替换原则时,尽量避免子类的“个性”,一旦子类有“个性”,这个子类和父类之间的关系就很难调和了,把子类当做父类使用,子类的“个性”被抹杀——委屈了点;把子类单独作为一个业务来使用,则会让代码间的耦合关系变得扑朔迷离——缺乏类替换的标准。

三、依赖倒置原则(Dependence Inversion Principle,DIP)

1. 原始定义

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

*名词解释:

每一个逻辑的实现都是由原子逻辑组成的,不可分割的原子逻辑就是低层模块,原子逻辑的再组装就是高层模块。

在Java语言中,抽象就是指接口或抽象类,两者都是不能直接被实例化的;细节就是实现类,实现接口或继承抽象类而产生的类就是细节,其特点就是可以直接被实例化,也就是可以加上一个关键字new产生一个对象。

2. 在Java语言中的表现

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

3. 依赖倒置原则好处

依赖倒置原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立,不互相影响,实现模块间的松耦合。

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

两个类之间有依赖关系,只要制定出两者之间的接口(或抽象类)就可以独立开发了,而且项目之间的单元测试也可以独立地运行,而TDD(Test-Driven Development,测试驱动开发)开发模式就是依赖倒置原则的最高级应用。

4. 依赖的三种写法

  • 构造函数传递依赖对象
  • Setter方法传递依赖对象
  • 接口声明依赖对象(接口注入)

5. 项目应用

需要遵循以下的几个规则

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

在项目中,大家只要记住是“面向接口编程”就基本上抓住了依赖倒置原则的核心。

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

1. 接口隔离原则的定义

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

即:建立单一接口,不要建立臃肿庞大的接口。再通俗一点讲:接口尽量细化,同时接口中的方法尽量少。(防止封装过度)

1.1 接口隔离原则包含四层含义

  • 接口要尽量小(根据接口隔离原则拆分接口时,首先必须满足单一职责原则。)
  • 接口要高内聚(高内聚就是提高接口、类、模块的处理能力,减少对外的交互。)
  • 定制服务(采用定制服务就必然有一个要求:只提供访问者需要的方法)
  • 接口设计是有限度的(接口的设计粒度越小,系统越灵活,这是不争的事实。但是,灵活的同时也带来了结构的复杂化,开发难度增加,可维护性降低,)

2. 接口隔离原则优点

接口是我们设计时对外提供的契约,通过分散定义多个接口,可以预防未来变更的扩散,提高系统的灵活性和可维护性。

五、迪米特法则(Law of Demeter,LoD)

1. 迪米特法则定义

一个对象应该对其他对象有最少的了解

1.1 迪米特法则包含四层含义

  • 只和朋友交流(类与类之间的关系是建立在类间的,而不是方法间,因此一个方法尽量不引入一个类中不存在的对象,当然,JDK API提供的类除外)
  • 朋友间也是有距离的(为了保持朋友类间的距离,在设计时需要反复衡量:是否还可以再减少public方法和属性,是否可以修改为private、package-private、protected等访问权限,是否可以加上final关键字等)
  • 是自己的就是自己的(如果一个方法放在本类中,即不增加类间关系,也对本类不产生负面影响,就放置在本类中。)
  • 谨慎使用Serializable(防止对象实现Serializable接口后属性访问权限的扩大在客户端与服务器端的不一致)

2. 迪米特法则使用

迪米特法则的核心观念就是类间解耦,弱耦合,只有弱耦合了以后,类的复用率才可以提高。其要求的结果就是产生了大量的中转或跳转类,导致系统的复杂性提高,同时也为维护带来了难度。读者在采用迪米特法则时需要反复权衡,既做到让结构清晰,又做到高内聚低耦合。

六、开闭原则(Open Closed Principle)

1. 开闭原则定义

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

2. 开闭原则好处

  • 开闭原则可以提高复用性
  • 开闭原则可以提高可维护性

3. 使用开闭原则

抽象约束(包含三层含义:第一,通过接口或抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的public方法;第二,参数类型、引用对象尽量使用接口或者抽象类,而不是实现类;第三,抽象层尽量保持稳定,一旦确定即不允许修改。)

元数据(配置参数)控制模块行为(通过扩展一个子类,修改配置文件,完成业务变化)

制定项目章程

封装变化(包含两层含义:第一,将相同的变化封装到一个接口或抽象类中;第二,将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。)

 

开闭原则是一个终极目标,任何人包括大师级人物都无法百分之百做到,但朝这个方向努力,可以非常显著地改善一个系统的架构,真正做到“拥抱变化”。

以上是关于《设计模式之禅》之——六大设计原则解读的主要内容,如果未能解决你的问题,请参考以下文章

《设计模式之禅》六大设计原则2

《设计模式之禅》笔记整理--面对对象设计六大原则

面向对象的六大原则

教练,我想学设计之禅

教练,我想学设计之禅

设计模式之禅第1章 单一职责原则