设计模式之禅
单一职责原则
- Single Responsibility Principle: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 unchanged when o1 is substituted for o2 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 classes without knowing it.(所有引用基类的地方必须能透明地使用其子类的对象。)
第二个定义是最清晰明确的,通俗点讲,只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类,但是反过来就不行了
子类必须完全实现父类的方法
子类可以有自己的个性
覆盖或实现父类的方法时输入参数可以被放大
覆写或实现父类的方法时输出结果可以被缩小
最佳实践:在项目中,采用里氏替换原则时,尽量避免子类的“个性”
依赖倒置原则
依赖倒置原则(Dependence Inversion Principle,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.
三层含义:
- 高层模块不应该依赖低层模块,两者都应该依赖其抽象;
- 抽象不应该依赖细节;
- 细节应该依赖抽象。
依赖倒置原则应用Java中的表现就是:
- 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;
- 接口或抽象类不依赖于实现类;
- 实现类依赖接口或抽象类。
更加精简的定义就是“面向接口编程”
采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。
最佳实践:
- 每个类尽量都有接口或抽象类,或者抽象类和接口两者都具备
- 变量的表面类型尽量是接口或者是抽象类
- 任何类都不应该从具体类派生
- 尽量不要覆写基类的方法
- 结合里氏替换原则使用
依赖倒置原则是6个设计原则中最难以实现的原则,它是实现开闭原则的重要途径,依赖倒置原则没有实现,就别想实现对扩展开放,对修改关闭。在项目中,大家只要记住是“面向接口编程”就基本上抓住了依赖倒置原则的核心。
接口隔离原则
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方法,接口是对外的承诺,承诺越少对系统的开发越有利,变更的风险也就越少,同时也有利于降低成本。
迪米特法则
迪米特法则(Law of Demeter,LoD)也称为最少知识原则(Least Knowledge Principle,LKP),虽然名字不同,但描述的是同一个规则:一个对象应该对其他对象有最少的了解
Only talk to your immediate friends(只与直接的朋友通信。)
迪米特法则要求类“羞涩”一点,尽量不要对外公布太多的public方法和非静态的public变量,尽量内敛,多使用private、package-private、protected等访问权限。
如果一个方法放在本类中,既不增加类间关系,也对本类不产生负面影响,那就放置在本类中。
最佳实践:迪米特法则的核心观念就是类间解耦,弱耦合,只有弱耦合了以后,类的复用率才可以提高。其要求的结果就是产生了大量的中转或跳转类,导致系统的复杂性提高,同时也为维护带来了难度。读者在采用迪米特法则时需要反复权衡,既做到让结构清晰,又做到高内聚低耦合。
开闭原则
Software entities like classes,modules and functions should be open for extension but closed for modifications.(一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。)
开闭原则对扩展开放,对修改关闭,并不意味着不做任何修改,低层模块的变更,必然要有高层模块进行耦合,否则就是一个孤立无意义的代码片段
重要性:
- 对测试友好
- 提高复用性
- 提高可维护性
- 面向对象开发的要求
最佳实践(SOLID)
- ● Single Responsibility Principle:单一职责原则
- ● Open Closed Principle:开闭原则
- ● Liskov Substitution Principle:里氏替换原则
- ● Law of Demeter:迪米特法则
- ● Interface Segregation Principle:接口隔离原则
- ● Dependence Inversion Principle:依赖倒置原则