设计模式六大原则理解
优雅的开始
六大原则
- 单一职责原则 (Single Responsibility Principle)
- 里氏替换原则 (Liskov Substitution Principle)
- 依赖倒置原则 (Dependence Inversion Principle)
- 接口隔离原则 (Interface Segregation Principle)
- 迪米特法则 (Law of Demeter)
- 开闭原则 (Open Closed Principle)
1.单一职责原则
对于接口的个数的控制,应根据 具体事务职责个数的划分。
优点
- 类的复杂性降低,实现什么职责都有清晰明确的定义
- 可读性提高,复杂性降低,那当然可读性提高了。
- 可维护性提高(细化,整体拆分),可读性提高,易于维护
- 变更引起的风险减低,变更是必不可少的,如果接口的单一职责做的好,一个接口修改只对对应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助。
缺点
- 一个职责一个接口,但难于划分职责,对应每个项目分的职责而言,职责没有一个量化的标准
注意:单一职责原则提出一个编写程序的标准,用“职责”或者变化原因 来衡量一个接口设计的是否优良,但是“职责”和”变化原因“都是不可度量的,因项目而异,因环境而异。
2.里氏替换原则
父类存在的地方,子类就应该能够存在 (把父类换成子类的化,结果不变)
-
子类须完全实现
-
子类可以有自己的个性(属性和方法)
-
覆盖或者实现父类的方法时输入参数可以被放大 (子类中方法的前置条件必须和超类中覆盖的方法的前置条件相同或者时更宽松)
-
覆写或者实现父类的方法时输出结果可以被缩小(比如父类一个方法返回是T,子类的相同方法 重载或覆写的返回值是S,那么对应里氏替换的原则就是要求S的范围必须小于等于T)
-
如果是重载,方法的参数数量或者类型不相同,在里氏替换中要求,子类的输入参数宽于或者等于父类的输入参数
-
如果是覆写,父类和子类的同名方法是输入参数是相同的,那必须要求两个方法的范围值S小于等于T,这是覆写的要求
-
优点
-
里氏替换原则的目的是增强程序的健壮性,
-
同时当系统进行升级的时候也能保持非常好的兼容性
-
在实际项目中,每个子类对应不同的业务含义,使用父类作为参数,传递不同的子类完成不同的业务逻辑。
3.依赖倒置原则 (核心是面向接口编程)
思想 (面向接口编程 OOD Object-Oriented Design 面向对象设计的精髓之一)
-
高层模块不应该依赖底层模块,两者应该都依赖其抽象;
-不可分割的原子逻辑就是低级模块,原子逻辑的再组装就是高层模块。
-
抽象不应该依赖细节
-
细节应该依赖抽象
Java
-模块之间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或者抽象类产生
-接口或抽象类不依赖于实现类
-实现类依赖接口或抽象类
三种依赖实现方法
- 构造函数传递依赖对象
- Setter方法传递依赖对象
- 接口声明依赖对象
遵循规则
- 每个类尽量有接口或者抽象类,或者抽象类和接口两者都具备
- 变量的表面类型尽量是接口和抽象类
- 任何类都不应该从具体类派生
- 任何类都不应该是从具体类派生
- 尽量不要覆写基类方法
- 结合里氏替换原则
优点:
- 实现并行开发
- 减少类间的耦合性,提高系统的稳定性,减小并行开发引起的风险,提高代码的可读性和可维护性
缺点:
- 原则中最难实现的原则,是实现开闭原则的重要途经。
- 在实际项目中使用依赖倒置原则时需要审时度势,不要抓住一个原则不放,每一个原则的优点都是有限度的。
4.接口隔离原则(根据接口隔离原则拆分接口时首先必须满足单一职责原则)
定义:建立单一接口,不要建立臃肿的接口,接口尽量细化,同时接口中的方法尽量少。
与单一职责原则的区别:审视角度不同,单一职责是要求类和接口责任单一,注重的是职责,这是业务逻辑上的划分。而接口隔离原则是要求接口的方法尽量少,提供给每个模块的都应该是单一接口,提供给几个模块就应该有几个接口。
对接口进行规范约束的4层含义:
-
接口要尽量的少
-
接口要高内聚:提高接口,类,模块的处理能力,减少对外的交互(不讲任何条件、立刻完成任务的行为就是高内聚的表现)具体到接口隔离原则就是,在接口中尽量少的公布public 方法,接口是对外部的承诺,承诺越少对系统开发越有利,变更的风险就越少,有利于降低成本
-
指定服务:只提供访问者需要的方法
-
接口设计是有限度的:接口的设计粒度越小,系统越灵活,同时也带来了结构的复杂化,开发难度增加,可维护性减低。
遵循规则
- 一个接口只服务于一个子模块或业务逻辑
- 通过业务逻辑压缩接口中的public方法
- 已经被污染了的接口,尽量去修改,若变更的风险较大,则可以采用适配器模式进行转化处理
- 因项目而异,每个项目或产品都有其特定的环境因素,环境不同,接口拆分的标准就不同,要深入了解业务逻辑
5.迪米特法则(最少知识原则)知道的越少越好
定义:一个类应该对自己需要耦合或调用的类知道得最少,你(被耦合或调用的类)的内部是如何复杂都和我没关系,我就只想用你提供的给我的所有public方法,其他我不关心。
思想:类间解耦,弱耦合
-
我只和朋友交流
- 朋友类------出现在成员变量、方法的输入输出参数中的类成为朋友类,而出现在方法体内部的类不属于朋友类
-
朋友间也是有距离的
- 当两者的朋友关系太亲密了,耦合关系变得异常牢固。如果要修改其中一个的话,就要修改其它信息。从而把修改变更的风险扩散开了。
-
是自己的就是自己的
- 坚守原则:如果一个方法放在本类中,即不增加类间关系,也对本类不产生负面影响,那就放置在本类中。
-
谨慎使用Serializable (网络传输的对象需要序列化)
- 客户端和服务器中间在修改,会互相影响,两者要做对应的变更。
6.开闭原则
定义:软件实体应该对扩展开放,对修改关闭 (通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来完成)
什么是软件实体:
-
项目或者软件产品中按照一定的逻辑规则划分的模块
-
抽象和类
-
方法
优点:
**1. 开闭原则对测试的影响 **新增加的类,新增加的测试方法,只要保证新增加的类是正确的就行。
- 开闭原则可以提高复用性,缩小逻辑粒度,直到一个逻辑不可再拆分为止
- 开闭原则可以提高可维护性 不改原代码(先读懂原有代码,再修改,是一件很痛苦的事件)
- 面对对象开发的要求 万物皆对象,我们需要把所有的事物都抽象成对象。
遵循规则:
1.抽象约束:通过接口或抽象类可以约束一组可能变化的行为,并实现对扩展开放
- 通过接口或抽象类约束扩展,对扩展进行边界限定,不允许出现再接口或抽象类中不存在的public方法
- 参数类型,引用对象尽量使用接口或者抽象类,而不是实现类。
- 抽象层尽量保持稳定,一旦确定即不允许修改
2.元数据控制模块行为
- 使用元数据来控制程序的行为,减少重复开发。(行为的极致是控制反转IOC)
- 元数据:用来描述环境和数据的数据,通俗的来讲是配置参数,参数可以从(配置)文件中获取,也可以从数据库中获得
3.制定项目章程(思想 约定大于配置)
- 项目内约束,每个项目成员都应该遵循。一旦项目成员都熟悉这样的规则,比通过接口或抽象类进行约束效率更高,而且扩展性一点也没有减少。
4.封装变化
- 将相同的变化封装到一个接口或抽象类中。
- 将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现再同一个接口或抽象类中。
注意:
-
开闭原则对扩展开放,对修改关闭,并不是意味着不做任何修改,低层模块的变更,必然要有高层模块进行耦合,否则就是一个孤立无无意义的代码片段。
-
开闭原则也只是一个原则,拥抱变化的方法非常多,不限于这六大设计原则
-
项目规章非常重要。尽量让项目成员稳定,稳定后才能建立高效的团队文化。章程是团队共识,也是所有成员必须遵循的约定。优秀的章程能带给项目带来非常多的好处,如提高开发效率,减低缺陷率,提高团队士气,提高技术成员水平。
-
预知变化:架构师设计一套系统不仅要符合现有的需求,还要适应可能发生的变化,这才是一个优良的架构。