单一职责原则
简介
单一职责原则(SRP,Single Responsesibility Principle):There should never be more than one reason for a class to change.(应该有且仅有一个原因引起类的变更)
优势
- 类的复杂性降低。实现什么职责都有清晰明确的定义
- 可读性提高,复杂性降低
- 可维护性提高,可读性提高
- 变更引起的风险降低
注意
- 单一职责是一个标准,用“职责”或“变化原因”来衡量接口或类设计的是否优良,但是这两者都是不可度量的,因项目和环境而定。
里氏替换原则
简介
里氏替换原则(LSP,Liskov Substitution Principle):
- If for each object o1 of type S there is an object o2 of type T suchthat for all programs P defined in terms of T, the behavior of P is unchangedwhen o1 is substituted for o2 then S is a subtype of T.(如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。)
- functions that use pointers or references to base classes must beable to use objects of derived classes without knowing it.(所有引用基类的地方必须能透明地使用其子类的对象。)
- 理解:只要父类能出现的地方子类就可以出现,即将父类对象调用的行为切换成子类对象调用行为也不会产生任何错误或异常,但是反过来却不行。
含义
- 子类必须完全实现父类的方法
- 里氏替换原则可以正着用,不能反过来用:子类出现的地方,父类未必可以胜任
- 覆盖或实现父类的方法时,输入参数可以被放大:如果反过来会引起业务逻辑的错乱。子类中方法的前置条件(参数)必须与超类中被覆写的方法的前置条件相同或更宽松。(如:父类是HashMap,子类是HashMap或Map)
- 覆写或实现父类的方法时输出结果可以被缩小:里氏替换原则要求S小于等于T,则S和T要么是同一类型,要么S是T的子类。
注意
- 在类中调用其他类时必须要使用父类或接口,如果不能使用父类或接口,说明类的设计已经违背了LSP原则
- 如果子类不能完整的实现父类的方法,或者父类的某些方法在子类中已经发生了”畸变”,建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承
依赖倒置原则
简介
依赖倒置原则(DIP,Dependence Inversion Principle):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变量有两种类型:表面类型(定义时候赋予的类型),实际类型(对象的类型)
- 依赖的三种写法:构造函数传递依赖对象、setter方法传递依赖对象、接口声明依赖对象
- 依赖倒置原则是实现开闭原则的重要途径。
使用
要想使用这个规则只需要做到一下几点:
- 每个类尽量都有接口或抽象类,或两者皆有
- 变量的表面类型尽量是接口或抽象类
- 任何类都不应该从具体类派生
- 尽量不要覆写基类方法
- 结合里氏替换原则使用:接口负责定义Public属性和方法,并声明与其他对象的依赖关系,抽象类负责公共构造部分的实现,实现类准确的实现业务逻辑,同时在适当的时候对父类进行细化
接口隔离原则
简介
接口隔离原则(ISP:Interface Segregation Principle):
- 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方法,并经常回顾接口
- 如果接口已经被污染,尽量去修改。如果不行,则使用适配器模式进行转换处理
- 了解环境,根据业务来设计
迪米特法则
简介
迪米特法则(LoD:Law of Demeter):一个对象应该对其他对象有最少的了解。即一个类应该对自己需要耦合或调用的类知道的最少
含义
- 只和朋友交流:不要出现getA().getB().getC().getD()的奇葩写法
- 朋友类:出现在成员变量、方法的输入输出参数中的类称为成员朋友类,出现在方法体内部的类不属于朋友类
- 朋友间也是有距离的:公开的public属性或方法越多,修改时涉及的面也就越大。在设计时候要反复考虑是否可以减少public的方法和属性,将其修改成private等,并且是否可以加上final关键字
- 是自己的就是自己的:如果一个方法放在本类中,既不增加类间关系也不对本类产生负面影响,就放在本类中
- 谨慎使用Serializable:
由于很容易被发现,所以不必太过在意。
注意
- 一个类只和朋友交流不和陌生人交流 ,不要出现getA().getB().getC().getD()的奇葩写法
- 类与类之间的关系是建立在类间的,而不是方法间的,一个方法尽量不引入一个类中不存在的对象,JDK API提供的除外
- 尽量不要对外公布太多的public方法和非静态的public变量
- 如果一个类跳转两次以上才能访问到另外一个类,就需要进行重构了。
开闭原则
简介
开闭原则:Software entities like classes,modules and functions should be open for extension but closed for modifications(一个软件实体,如类、模块和函数应该对扩展开发对修改关闭)
- 理解:一个软件实体应该通过扩展来实现变化,而不是通过修改已有代码来实现变化。
优势
- 使用开闭原则能有效的减少重构时的测试
- 可以提高复用性
- 可以提高可维护性
- 面向对象开发的要求
使用
- 抽象约束
- 通过接口或抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的public方法
- 参数类型、引用对象尽量使用接口或抽象类,而不是实现类
- 抽象层尽量保持稳定,一旦确认就不允许修改
- 元数据控制模块行为:尽量使用元数据来控制程序的行为(描述缓解和数据的数据,通俗的说就是配置)
- 制定项目章程
- 封装变化
- 将相同的变化封装到一个接口或抽象类中
- 将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中
注意
- 开闭原则对扩展开放对修改关闭,并不意味着不做任何修改,底层模块的变更,必然有高层模块进行耦合,否则就是无意义的代码