单一职责原则(Single Responsibility Principle SRP)的定义:应该有且仅有一个原因引起类的变更。
单一职责原则的好处:
类的复杂性降低,实现什么职责都有很清晰明了的定义。
提高可读性,降低复杂性。
提高可维护性、可读性。
降低变更引起的风险,一个接口修改只对相应的实现类有影响,对其他接口无影响,这对系统的扩展性、维护性都有非常大的帮助。
注意:
单一职责原则提出的一个编写程序的标准,用"职责"或"变化原因"来衡量接口或类设计是否优良,但是"职责"和“变化原因”都是不可度量的,因项目、环境而异。
生搬硬套单一职责原则会引起类的剧增,给维护带来非常多的麻烦,而且过分细分类的职责也会人为地增加系统的复杂性。
对于单一职责原则,我的建议是接口一定做到单一职责,类的设计尽量做到有一个原因引起变化。
什么是高内聚?高内聚就是提高接口、类、模块的处理能力,减少对外交互。
里氏替换原则(Liskov Substitution Principle LSP):
如果对每一个类型为S的对象o1,都有类型T的对象o2,使得以T定义的所有程序P在所有对象o1都替换成o2时,程序P的行为没有发生变化,那么类型S是类型T的子类。
只要父类能出现得到地方子类就可以出现,而且替换成子类也不会产生任何错误或者异常, 使用者根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必能适应。
子类必须完全实现父类方法
在类中调用其他类时务必要使用父类或者接口,如果不能使用父类或者接口,则说明累的设计违背了LSP原则。
如果子类不能完整实现父类方法,或者父类的某些方法在子类中发生了改变,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。
覆盖或实现父类的方法时输入参数可以被放大
子类重载父类方法是,参数范围必须大于父类的范围
子类方法的前置条件必须与超类中被复写的方法的前置条件相同或者更宽松,否则在参数为超类时,传入子类超类的方法不会被执行(会执行子类方法)导致业务混乱。
依赖倒置原则
高层模块不应该依赖底层模块,两者都应该依赖其抽象。
模块之间的依赖是通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或者抽象类产生的
抽象不应该依赖细节
接口或者抽象类不依赖实现类
细节应该依赖抽象
实现类依赖接口或者抽象类
更加精简的定义是就是:面向接口编程
抽象是对实现的一种约束,对依赖者而言,也是一种契约,不仅仅约束自己,同时约束自己与外部的关系,保证细节不脱离契约的范畴。
遵循以下几个原则:
每个类尽量都有接口或者抽象类,或者抽象类和接口两者具备
变量的表面类型尽量是接口或者抽象类
任何类都不应该从具体类派生(不超过两层)
尽量不要复写基类的方法
有点:
可以减少类之间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码可读性和可维护性。
接口隔离原则
建议单一接口,不要建立臃肿庞大的接口
模块的接口,需要专门提供,不要建立一个庞大臃肿的接口,容纳所有客户端访问。
接口要高内聚:提高接口的内部处理能力,减少对外交互。
接口划分的几个规则衡量:
一个接口只服务于一个字模块或者业务逻辑
通过业务逻辑压缩接口中的public方法,接口时常去回顾,尽量让接口达到“满身筋骨肉”的效果。
已经被污染的接口,尽量去修改,若变化风险较大,采用适配器模式进行转换处理。
了解环境,拒绝盲从。
接口隔离原则和其他设计原则一样,都需要花费较多的时间和精力进行设计和筹划,但是它带来了设计的灵活性。
迪米特法则
也称之为最小知识原则(Least Knowledge Principle LKP)一个对象应该对其他对象有最小的了解。
只与直接的朋友通信:例如组合、聚合、依赖等。朋友的定义:出现在成员变量、方法的输入输出参数中的称谓朋友类,而出现在方法体内部的类不属于朋友类。
一个方法,放在本类和放在其他类也可以。如果一个方法放在本类中,既不增加类间关系,也对本类不产生负面影响,那就放置在本类中。
在实际项目中需要适度考虑使用原则,别为套用原则而做项目。原则是用来参考的,如果违背了原则项目也未必会失败。
开闭原则
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
软件对扩展开发,对修改关闭,其含义是说一个软件实体应该通过扩展来实现变化,而不是修改已有的代码来实现变化。对修改关闭,并不意味着不做任何修改,低层模块的变更,必然要有高层模块进行耦合,否则就是一个孤立无意义的代码片段。
把可以变化归纳成以下三种类型:
逻辑变化:只变化一个逻辑,而不涉及其他模块,例如原有A+B 改成A*B,可以通过修改原有方法完成,前提条件是:所有依赖或者关联类都按照相同的逻辑处理。
子模块变化:一个模块变化,会对其他模块产生影响,特别是低层模块的变化,必然会引起高层模块的变化。
可见视图变化:可见视图是提供客户使用的界面,该部分的变化一般会引起连锁反应。例如:一个展示数据列表,按照原有的需求是N列,突然有一天要增加1列,而且这列数据需要跨N张表,处理M个逻辑才能实现,这样的变化就比较恐了。程序设计比较灵活,可以通过扩展来完成。