(SRP) The Single Responsibility Principle 单一责任原则
(OCP) The Open-Closed Principle 开放-封闭原则
(LSP) The Liskov Substitution Principle Liskov替换原则
(ISP) The Interface Segregation Principle 接口聚合原则
(DIP) The Dependency Inversion Principle 依赖转置原则
一.Single Responsibility Principle (SRP) 单一责任原则
- 定义:应该有且仅有一个原因引起类的变更,也就是接口或类和职责的关系是一一对应的。
- 不应有多于1个的原因使得一个类发生变化一个类,一个责任
- 难点:职责的划分:
- 在不同情景和生产环境下我们对职责的细化是不同的(职责单一的相对性)
- 单一职责原则提出的是一个评价接口是否优良的标准,但是职责和变化原因是不可度量的,因项目而异,因环境而异(不可度量性)
- 优势:
- 类的复杂性降低:每个类或接口都只实现单一的职责,定义明确清晰
- 可读性提高:定义明确清晰,自然带来较高的代码可读性
- 可维护性提高:代码可读性强,更容易理解,自然方便维护,而且职责单一所以类之间耦合度较低,所以更容易修改。
- 拓展性更好:有新的职责需要拓展,只需要继承对应的接口实现新的实现即可。
- 最简单的原则,却是最难做好的原则
2.实例:
总的来说:
对于单一职责原则,接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化
二.(OCP) The Open-Closed Principle 开放-封闭原则
(1)对扩展性的开放:模块的行为应是可扩展的,从而该模块可表现出新的行为以满足需求的变化
(2)对修改的封闭:但模块自身的代码是不应被修改的,扩展模块行为的一般途径是修改模块的内部实现
如果一个模块不能被修改,那么它通常被认为是具有固定的行为)
(3) 关键的解决方案:抽象技术
2.实例
三. (LSP) The Liskov Substitution Principle Liskov替换原则
1.基本概念:
- 定义:
- 所有引用基类的地方必须能透明地使用其子类的对象
- 只要父类能出现的地方子类就可出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类(封装造成的多态性)
- 规范
- 子类必须完全实现父类的方法
在类中调用其他类时必然要使用父类或者接口,如果子类中不支持父类中的方法,自然就违背了LSP - 子类要有自己的特性
- 子类是在父类的基础上实现的,有自己的特性
- 这也就导致了LSP的单向性,在子类能出现的地方,父类未必能够胜任。
- 比如实际开发中向下转型是不正确的,会抛出ClassCastException异常。
- 覆写或实现父类的方法时参数可以被放大
- 子类中方法的参数的前置条件必须与超类中被覆写的方法的前置条件相同或者更宽松
- 因为参数类型不同会导致参数列表不同,那么实际上我们在参数类型不同时实现的是重载而非覆写
- 所以在子类中方法条件更宽松的情况下,因为重载的调用规则,更加匹配严谨的重载会优先调用,也就意味着我们这个重载不会意外地覆盖父类中的方法(比如HashMap比Map类型会优先被匹配,可能会导致重载达到也向覆写一样覆盖了父类的函数,但是显然这是不符合逻辑的),保证了LSP的正确性。
- 覆写或实现父类的方法时的输出结果可以被缩小
- 这个缩小的具体含义如下:设父类中方法的返回值是T,子类中覆写的方法的返回值是S,那么S要么和T是同类型,要么S是T的子类
- 因为在LSP下,满足缩小条件条件才能保证返回对象在使用时是安全且完整实现的。
- 子类必须完全实现父类的方法
- 优点:采用LSP能够增强程序的健壮性,版本升级使能够巴证非常好的兼容性,即使增加子类,原有的子类还可以继续运行。
四. (ISP) The Interface Segregation Principle 接口聚合原则
- 实例接口(Object Interface):一个类的实例对象是对一个类型的事物的描述,这时一种接口。
- 类接口(Class Interface):Java中interface关键定义的接口,也是我们通常理解的狭义的接口
1.基本概念:
(1)客户端(模块)不应该依赖它不需要的接口(接口的纯粹性):
一个模块应该依赖它需要的接口,需要什么接口就提供什么接口,
把不需要的接口剔除掉,那就需要对接口进行细化,保证接口的纯洁性
(2)类间的依赖关系应该建立在最小的接口上
同样强调对接口的细化,那么接口作为抽象,在建立关系时才能保证纯洁性不会被无关的依赖破坏
(3)接口细化
在不违反单一职责原则(SRP)下,要对接口尽量细化,
违背SRP会导致单个模块暴露太多方法(接口中的方法都是publi),很多单一职能的内部接口被暴露。
(4)提高内聚性
高内聚:提高接口、类、模块的处理能力,减少对外的交互。
接口是对外的承诺,承诺越少对系统的开发越有利,变更的风险也更小,同时也有利于降低成本。
(5)模块服务定制
为了减少模块间的耦合,可以对模块间的类似功能分别定制服务(不共用接口,而是使用专门的接口)
五.(DIP) The Dependency Inversion Principle 依赖转置原则
1.原始定义
- 高层模块不应该依赖低层模块,两者都应该依赖其抽象
- 抽象不应该依赖细节
- 细节应该依赖抽象
2.Java中的具体含义
- 模块间的依赖通过抽象发生
- 实现类之间不发生直接的依赖关系
- 其依赖关系通过接口或者抽象类产生
- 接口或抽象类不依赖于具体实现
- 实现类依赖接口或抽象类
3.依赖倒置(DIP)的好处
- 采用DIP可以减少类之间的耦合性,提高稳定性,降低并行开发带来的风险,提高代码的可读性和可维护性
- 通过抽象(接口或者抽象类)使各个类和模块的实现彼此独立,不互相影响,实现模块之间的松耦合
- DIP是开闭原则实现的基础
文章参考