第7章 面向对象设计原则——单一职责原则与开闭原则
耿祥义、张跃平《Java面向对象程序设计(第4版)》第7章
一、什么是面向对象设计原则?
在面向对象程序设计中,除了掌握继承、多态、接口等语法特性外,更重要的是理解面向对象设计的原则。这些原则是前人总结出的经验法则,指导我们如何设计出可维护、可扩展、可复用的软件系统。
本章介绍两个最基本、最重要的原则:单一职责原则和开闭原则。
二、单一职责原则
2.1 定义
单一职责原则(Single Responsibility Principle,SRP) 是最简单的面向对象设计原则,也是最难正确运用的原则之一。
它的核心思想是:一个类只负责一个功能领域中的相应职责。
通俗地说:一个类,只有一个引起它变化的原因。
2.2 如何理解”职责”?
一个类的”职责”可以理解为”它为什么存在”或”它做什么事情”。
- 如果一个类有多个引起它变化的原因,就说明它承担了多个职责
- 职责过多,就会导致类变得臃肿、复杂、难以维护
2.3 违反单一职责原则的后果
当一个类承担了多项职责时,会出现以下问题:
- 可维护性差:修改某一职责的代码可能会影响其他职责的正常功能
- 可复用性差:无法单独使用某个职责的功能,因为所有功能都耦合在一个类中
- 可测试性差:测试一个职责时,必须依赖其他职责的代码
- 代码臃肿:一个类包含了大量无关的方法,代码量过大
2.4 如何识别违反单一职责原则的类?
可以通过以下几个问题来判断:
- 这个类的方法是否涉及多个不同的功能领域?
- 修改这个类的某个方法,是否会影响其他不相关的方法?
- 这个类的名称是否过于宽泛(如”Manager”、”Util”、”Handler”)?
- 这个类是否有多个不同的”使用者”,每个使用者只用到其中一部分功能?
如果上述问题多数为”是”,说明该类违反了单一职责原则。
2.5 课堂作业

三、开闭原则
3.1 定义
开闭原则(Open-Closed Principle,OCP) 是面向对象设计中最重要的原则,由Bertrand Meyer于1988年提出。
它的核心思想是:对扩展开放,对修改关闭。
- 对扩展开放:当系统需要增加新功能时,可以通过添加新代码来扩展系统的行为
- 对修改关闭:在添加新功能时,不应修改已有的源代码
通俗地说:写好的代码,不要再动了;要加新功能,就写新类。
3.2 为什么开闭原则如此重要?
在软件工程中,修改已有代码是有风险的:
| 风险 | 说明 |
|---|---|
| 引入新Bug | 修改一处代码可能破坏原本正常的功能 |
| 影响其他模块 | 被修改的类可能被多个其他类依赖,修改后影响范围难以预估 |
| 增加测试成本 | 每次修改都需要重新测试所有相关功能 |
| 难以维护 | 频繁修改的代码会变得混乱,难以理解 |
开闭原则通过”不修改已有代码,只添加新代码”的方式,有效规避了上述风险。
3.3 如何实现开闭原则?
实现开闭原则的关键是抽象化:
- 定义抽象层:使用抽象类或接口定义系统的核心规范
- 实现具体类:让不同的具体类实现这个抽象层
- 面向抽象编程:客户端代码针对抽象层编程,不依赖具体实现类
- 扩展新功能:当需要新增功能时,只需添加一个新的具体类,无需修改已有代码
这一过程依赖Java的多态机制——父类引用可以指向任意子类对象,从而实现”新增子类即可扩展功能”。
3.4 违反开闭原则的后果
当系统不遵循开闭原则时,会出现以下问题:
- 分支泛滥:大量使用if-else或switch来判断类型,每增加一种类型都要修改判断逻辑
- 代码重复:相似的功能无法复用,只能复制粘贴后再修改
- 扩展困难:每次新增功能都要修改多处已有代码,且改动的类和数量不断增加
- 回归风险:每次改动都可能引入新的Bug,需要重新测试整个系统
3.5 课堂作业


四、两个原则的对比总结
| 对比项 | 单一职责原则 | 开闭原则 |
|---|---|---|
| 核心思想 | 一个类只做一件事 | 对扩展开放,对修改关闭 |
| 关注点 | 类的内部职责分配 | 系统的可扩展性 |
| 解决问题 | 类的臃肿和高耦合 | 新增功能时被迫修改已有代码 |
| 实现方式 | 将多个职责拆分到不同类中 | 引入抽象层,利用多态扩展 |
| 目标 | 提高可维护性、可复用性 | 提高可扩展性、降低修改风险 |
单一职责原则解决的是类的设计质量问题,开闭原则解决的是系统的可扩展性问题。两者相辅相成——遵循单一职责原则设计的类,通常也更容易符合开闭原则。
参考资料:耿祥义、张跃平《Java面向对象程序设计(第4版)》第7章