单一职责原则:一件事只做一件事
单一职责是软件组织的第一原则:一个东西只做一件事。 它不是某个粒度的规则,而是贯穿所有粒度的主题——函数、类、文件、模块、组件、系统,每一层都在执行同一个原则。职责混乱就拆分,拆到「每个单元只承担一个职责」为止。
一、原则一句话
一个东西只做一件事。
更准确地说:
职责单一,按变化原因拆分,而不是单纯因为数量多就拆。
1 2
| ❌ 一个函数 100 行但只做一件事 → 不该拆(职责单一) ✅ 一个函数 20 行但做三件事 → 该拆(职责不单一)
|
判断标准是职责,不是大小。
二、单一职责贯穿所有粒度
1 2 3 4 5 6 7 8
| 函数 只做一个操作(一个明确操作) 类 只封装一个对象(对象/状态/行为的封装) 文件 只承担一个职责(编译单元) 模块 只负责一个功能(独立功能单元) 组件 只提供一个功能单元(数据+算法+接口) 子系统 只负责一类相关功能(一组相关功能) 分层 每层只处理一类问题(界面/业务/基础设施) 原子化接口 只做封装库接口 + 异常处理
|
1 2 3
| MovementSystem 只负责移动,不负责碰撞、不负责渲染 ProcessService 只负责进程业务,不负责网络、不负责 UI Parser 只负责字节↔消息转换,不负责路由分发
|
三、为什么要单一职责
一个东西做了太多事,代价:
1 2 3 4
| ❌ 改一个职责影响其他职责(牵一发动全身) ❌ 无法独立测试(测试 A 职责必须带上 B 职责) ❌ 无法独立复用(想复用 A 职责,被迫带上 B 职责) ❌ 理解困难(一个东西要同时理解好几件事)
|
单一职责后:
1 2 3 4
| ✅ 改 A 不影响 B(职责独立) ✅ 单独测试 A(职责独立) ✅ 单独复用 A(职责独立) ✅ 理解 A 只需理解一件事
|
单一职责降低的是「复杂度和耦合」——每个单元只承担一个职责,所以改、测、复用、理解都只需要面对一件事。
四、单一职责 vs 拆分
单一职责和拆解是同一个动作的两面:
1 2 3 4 5
| 职责混乱 → 拆分(沿边界切) 拆分到什么时候停?→ 每个单元职责单一
拆分依据 = 职责(一件事 vs 多件事) 停止条件 = 单一职责(每个单元只做一件事)
|
1 2 3 4 5 6
| main 膨胀(职责混乱) → 拆函数(每个函数一个职责) → 拆文件(每个文件一个职责) → 分层(每层一个职责) → 拆模块(每个模块一个职责) → 拆系统(每个系统一个职责)
|
拆解是动作,单一职责是停止条件。 拆到每个单元职责单一,就是拆分完成。
五、单一职责 vs 重复
软件组织的两条第一原则:
1 2 3 4 5
| 职责混乱 → 拆分(单一职责) 重复代码 → 封装(复用)
一个东西做了太多事 → 拆分(降低复杂度) 很多地方做同一件事 → 封装(降低重复)
|
1 2
| 一个 PlayerSystem 处理输入+移动+碰撞+渲染 → 职责问题 → 拆成多个 System 十几个地方写相同文件读取 → 重复问题 → 封装成 FileService
|
职责混乱就拆分(单一职责),重复代码就封装(复用)。 这是软件组织的两条第一原则,单一职责是其中之一。
六、和其他内容的关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 单一职责 ←→ 拆解与组织 拆解沿边界切,单一职责是停止条件
单一职责 ←→ 边界 职责边界 = 单一职责的直接体现(边界五种之一)
单一职责 ←→ 原子化接口 原子化接口 = 单一职责在接口层的实现
单一职责 ←→ 从指令到系统主线 主线每一步都是职责混乱 → 拆分 拆分的目标就是单一职责
单一职责 ←→ 功能函数与模块 功能函数 = 数据结构 + 算法 + 接口(各自单一职责) 模块 = 一个功能的组合
|
收束
1 2 3 4 5 6 7 8 9 10 11 12 13
| 单一职责 = 一个东西只做一件事 判断标准是职责,不是大小
贯穿所有粒度:函数/类/文件/模块/组件/系统/分层/接口
代价:改 A 影响 B / 无法独立测试复用 / 理解困难
单一职责是拆解的停止条件: 拆分依据 = 职责,停止 = 每个单元职责单一
两条第一原则: 职责混乱 → 拆分(单一职责) 重复代码 → 封装(复用)
|
一件事只做一件事。 这是软件组织的第一原则——从最小的函数到最大的系统,都在执行它。