SRP 讲的是什么
单一职责原则最常见的表述来自 Robert C. Martin:一个模块应该只有一个被修改的原因。他把「一个类一个职责」翻译成更可操作的说法——看变化方向:同一处代码如果因为报表需求、计费规则、存储方式等不同原因被反复修改,说明它承担了多个职责。判断依据是变化轴,而不是代码行数。
基本信息
| 核心表述 | 只对一个变化原因负责 |
|---|---|
| 判断依据 | 变化轴:谁的需求变化会导致它被改 |
| 信号 | 同一个类因不同原因被反复修改,改动互相干扰 |
| 关联概念 | 高内聚、低耦合,以及发散式变化这一坏味道 |
什么时候该拆
- 同一个类里,不同角色的需求分别要求修改它。
- 一次改动只是为了某一类使用者,却要重新编译或测试全部逻辑。
- 类的变更原因在描述里要用「以及」连接(既算工资又存数据库又出报表)。
- 单元测试需要搭建大量与当前测试无关的依赖。
- 方法集合天然分成几组,组间几乎不互相调用。
按变化轴拆分
按变化轴拆分
// 之前:Employee 承担 算工资 + 存数据库 + 出报表
// 之后按变化原因拆开
class Employee { Money calculatePay(); }
class EmployeeRepository { void save(Employee e); }
class EmployeeReporter { String report(Employee e); }职责拆分的收益与代价
| 方面 | 收益 | 代价 |
|---|---|---|
| 修改范围 | 需求变化只影响对应模块 | 跨模块调用增多,需要清晰边界 |
| 测试 | 可针对单一职责写小测试 | 需要为协作关系补集成测试 |
| 理解成本 | 每个类更容易理解 | 类数量增加,需要靠命名与包结构导航 |
| 过度拆分 | —— | 类碎化,读一条业务链路要跳十个文件 |
常见误区
- 把职责当成功能:同一主题下的多个方法通常仍属一个职责。
- 按方法数量拆分:方法少但变化原因多,仍然是多职责。
- 用技术分层(数据访问、服务、控制器)替代职责判断:业务原因仍会让类不断膨胀。
- 追求每个类都只有一个方法:过度拆分让调用链变长,理解成本反而上升。
「职责」不是「功能」:一个类可以有多个方法,只要它们服务于同一个变化原因。
判断拆不拆的实用问法:这次需求变化会让哪些代码变动?如果答案是互不相关的两组,就该考虑拆。