设计模式是前任大牛们在开发工程中总结出来的技术结晶,而这些设计都是以六大设计原则来总结提炼的。 所以在学习这23式之前,我们需要先练习心法。
六大设计原则:
- 单一职责原则
- 里氏替换原则
- 依赖倒置原则
- 接口隔离原则
- 迪米特法则
- 开闭原则
单一职责原则:
英文描述:Single Responsibility Principle, 简称:SRP 原文解释为:There should never be more than one reason for a class to change.
在我们的开发过程中,尽量保证一个类的职责单一,但是这个划分需要多方面考虑。 虽然保证类单子职责可以降低类的复杂度,职责清晰,可读性高,可维护性高,降低变更引起的风险,但是如果生搬硬套会导致类的剧增,给维护带来非常多的麻烦,过分细分类的职责也会增加系统的复杂性,本来一个类可以是实现的行为硬拆成两个类,然后使用聚合或者组合的方式耦合在一起,人为的造成了系统的复杂性。所以原则是死,人是活的,一切看使用人如何灵活运用了。
里氏替换原则
英文描述: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 unchaned when o1 is substituted for o2 then S is a subtype of T.(每一个S类型的o1对象替换T类型的o2对象,在程序P中定义T类型,而这个程序P没有任何改变,则o1是o2的子类,也就是S是T的子类)
- Functions that use pointers or reference to base classes must be able to use objects of derived classes without konwing it.(所有引用基类的地方必须能透明使用其子类的对象)
通俗的解释:任何子类替换父类的地方不会产生任何错误和异常。
依赖倒置原则
英文描述:Dependence inversionPrinciple,简称: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.(高模块不应该依赖低模块,两则应该依赖其抽象,抽象不应该依赖细节,细节应该依赖抽象)
这个就是我们”面向接口编程“精髓之一,制定契约。
采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。
依赖倒置原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立,不互相影响,实现模块间的松耦合。
接口隔离原则
英文描述:Interface Segregation Principle,简称:ISP
接口分为2种:
- 实例接口(Object Interface),在Java中声明一个类,然后new一个实例,它是对一个类型的事务的描述,这是一种接口。Java的类也是一种接口,如:Person zhansan = new Person() ,Person类就是zhansan的接口;
- 类接口(Class Interface),也就是Java中interface定义的接口。
定义:
- 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方法,接口时常去回顾,尽量精简不臃肿;
- 已经被污染的接口,尽量去修改,如果变更的风险较大,采用适配器模式进行转化处理;
- 了解环境,拒绝盲从,深入了解业务,才能设计出最好的接口。
只有实践、经验和领悟才能设计设计出好的接口。
迪米特法则
英文描述:Law of Demeter, 简称:LoD 也称为:Least Knowledge Principle, 简称:LKP 所以也叫:最少知道原则。 尽量降低类与类之间的耦合,只和朋友交流,不和陌生人说话。
迪米特法则对类的低耦合的要求:
- 只和朋友交流
直接朋友如何定义? 组合、聚合、依赖等
- 朋友间也是有距离
为了保持朋友间的距离,设计时需要反复衡量,是否减少public方法和属性,是否可以修改为private、protected等访问权限,是否可以加final关键字等。
- 是自己的就是自己的
如果一个方法放在本类中,即不增加类间关系,也不对本类产生负面影响,就放置在本类中。
- 谨慎使用Seriallzable
实现序列化的类中的属性修改,而服务器没有做响应的变更,就会报序列化失败。
迪米特法则的核心观念就是类间解耦,只有弱耦合以后,类的复用率才可以提高。 在实际应用中,如果一个类跳转两次以上才可以访问另一个类,就需要想办法重构,因为跳转次数越多,系统越复杂,维护就越困难。 原则只是供参考,采用原则师反复度量,不遵循是不对的,严格执行就是”过犹不及“。
开闭原则
英文描述:Open-closed Principle,简称:OCP
定义: Software entities like clases, modules and functions should be open for extension but closed for modifications.(一个软件实体如类、模块和函数应该对扩展开发,对修改关闭)
开闭原则对扩展开放,对修改关闭,但并不意味着不做任何修改。 变化的分类:
- 逻辑变化
- 子模块变化
- 可见视图变化
开闭原则的相当于一个抽象类,其他五个原则是具体的实现类。
开闭原则可以提高复用性、可维护性; 面向对象编程,对于可能变化的因素,留下接口,等待”可能“转变为”现实“。
如何使用开闭原则?
- 抽象约束: 1.通过接口或抽象约束扩展,进行边界限定; 2.参数类型、引用对象尽量使用接口或抽象类; 3.抽象层尽量保持稳定。
- 元数据控制模块行为:也就是通过配置来控制模块行为。
- 指定项目章程:
- 封装变化 1.将相同的变化封装到一个接口或抽象类中; 2.不同的变化封装到不同的接口或抽象类中。
软件设计中最大的难题就是应对需求的变化!