面向对象设计原则
一、概述
简而言之就是讲究两点:
- 可维护性(Maintainability)
- 可复用性(Reusability)
常用的面向对象设计原则有七种:
设计原则名称 | 定义 | 使用频率 |
---|---|---|
单一职责原则 (Single Responsibility Principle,SRP) |
一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中 | ⭐⭐⭐⭐ |
开闭原则 (Open-Closed Principle,OCP) |
软件实体应当对扩展开放,对修改关闭 | ⭐⭐⭐⭐⭐ |
里氏代换原则 (Liskov Substitution Principle,LSP) |
所有引用基类的地方必须能透明地使用其子类的对象 | ⭐⭐⭐⭐⭐ |
依赖倒转原则 (Dependence Inversion Principle,DIP) |
高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象 | ⭐⭐⭐⭐⭐ |
接口隔离原则 (Interface Segregation Principal,ISP) |
客户端不应该依赖那些它不需要的接口 | ⭐⭐ |
合成复用原则 (Composite Reuse Principal,CRP) |
优先使用对象组合,而不是通过继承来达到复用的目的 | ⭐⭐⭐⭐ |
迪米特法则 (Law of Demeter,LoD) |
每一个软件单位对其他单位都只有最少的知识,而且局限于那些本单位密切相关的软件单位 | ⭐⭐⭐ |
二、面向对象设计原则
1. 单一职责原则
简而言之就是控制类的粒度大小
如下图类:
CustomerDataChart |
---|
+ getConnection() : Connection + findCustomers() : List + createChart() : void + displayChart() : void |
使用单一职责原则对其重构后需要3个类:
- DbUtil:负责连接数据库,其中包含方法 getConnection()
- CustomerDao:负责操作数据库中的 Customer 表,包括增删改查操作,例如 findCustomers()
- CustomerDataChart:负责图表的生成和显示,包括 createCharte() 和 displayChart() 方法
2. 开闭原则
就是指软件实体应尽量在不修改原有代码的情况下进行扩展
为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键
在 Java 中,可以为系统定义一个相对稳定的抽象层,而将不同的实现行为移至具体的实现层中完成
例如,接口、抽象类等机制,可以通过它们定义抽象层,再通过具体类进行扩展
因此,如果需要修改系统的行为,无须对抽象层进行修改,只需增加新的具体类来实现新的业务功能即可
3. 里氏代换原则
举例说明就是,我喜欢动物,那我一定喜欢狗,因为狗是动物的子类;但是我喜欢狗,不能因此推断出
我喜欢所有动物。
里氏代换是实现开闭原则的重要方式之一,由于在使用基类对象的地方都可以使用子类对象,
因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定子类类型,用子类对象代替父类对象
4. 依赖倒转原则
通俗地讲,就是高层模块定义接口,低层模块负责实现
比如可以在高层模块中定义接口,低层模块中定义类,并继承接口,实现接口中的所有成员
在实际项目开发过程中,开闭原则、里氏代换原则、依赖倒转原则常常会同时出现
开闭原则是目标,里氏代换原则是基础,依赖倒转是手段
5. 接口隔离原则
简而言之,当一个接口太大时需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可
在使用时也需要主要控制接口的粒度,接口不能太小,如果太小会导致系统中的接口泛滥,不利于维护;接口也不能太大,太大就违背了接口隔离原则
6. 合成复用原则
同释义,优先使用对象组合,而不是通过继承来达到复用的目的
为什么不适用继承来达到复用?
问题在于继承复用会破坏系统的封装性,因为继承会将实现细节暴露给子类,如果基类发生改变,那么子类也不得不发生改变
7. 迪米特原则
不要和“陌生人”说话,而你的朋友是:
- 当前对象本身(this)
- 以参数形式传入到当前对象方法中的对象
- 当前对象的成员对象
- 如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友
- 当前对象所创建的对象
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!