面向对象设计原则

一、概述

简而言之就是讲究两点:

  • 可维护性(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. 迪米特原则

不要和“陌生人”说话,而你的朋友是:

  1. 当前对象本身(this)
  2. 以参数形式传入到当前对象方法中的对象
  3. 当前对象的成员对象
  4. 如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友
  5. 当前对象所创建的对象

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!