组件设计原则
本文是《Clean Architecture》组件章节的读书笔记,探讨如何设计良好的组件边界。
什么是组件?
组件是部署的最小单元——它们是系统中可以独立部署的最小实体。
| 语言/平台 | 组件形式 |
|---|---|
| Java | .jar 文件 |
| Ruby | .gem 文件 |
| .NET | .dll 文件 |
| 编译型语言 | 二进制文件的聚合 |
| 解释型语言 | 源文件的聚合 |
组件的两种部署方式
- 链接成单一可执行文件:如 C++ 的静态链接
- 打包成归档文件:如 Java 的
.war包 - 独立部署的插件:如动态加载的
.dll或.jar
无论哪种方式,良好设计的组件始终保持独立部署能力,因此也能独立开发。
组件内聚三原则
组件应该包含哪些类和模块?这三个原则帮你做出决策:
┌─────────────────────────────────────────────────────────────┐
│ 组件内聚原则三角形 │
├─────────────────────────────────────────────────────────────┤
│ │
│ REP │
│ 复用/发布等价原则 │
│ 「便于复用」 │
│ ▲ │
│ / \ │
│ / \ │
│ 太多不相关 / \ 太多无法 │
│ 的类 / \ 一起复用 │
│ / ✓ \ │
│ / 平衡区 \ │
│ / \ │
│ ▼ ▼ │
│ CCP ◄─────────────► CRP │
│ 共同闭包原则 共同复用原则 │
│ 「便于维护」 「避免无谓发布」 │
│ ↑ │
│ └── 太多不必要的发布 ──┘ │
│ │
└─────────────────────────────────────────────────────────────┘
REP:复用/发布等价原则
The Reuse/Release Equivalence Principle
复用的粒度等于发布的粒度
核心思想
想要复用软件组件的人,必须依赖于有版本号的正式发布。没有人愿意复用一个没有版本管理、随时可能变化的代码。
设计启示:
组件中的类和模块必须形成一个 内聚的组,有共同的主题或目的:
| ✅ 好的组件 | ❌ 差的组件 |
|---|---|
| 所有类服务于同一目的 | 随机堆砌的类集合 |
| 可以一起复用 | 使用者只需要其中一小部分 |
| 有明确的发布版本 | 版本号没有意义 |
CCP:共同闭包原则
The Common Closure Principle
因相同原因而修改的类应该放在同一组件中
核心思想
这是 SRP(单一职责原则)在组件层面的体现。一个组件不应该有多个变更原因。
为什么这很重要?
场景:需求变更导致代码修改
❌ 不好的设计:修改分散在 5 个组件
→ 需要重新构建、测试、部署 5 个组件
→ 风险高、成本大
✅ 好的设计:修改集中在 1 个组件
→ 只需重新构建、测试、部署 1 个组件
→ 其他组件完全不受影响
实践建议
对大多数应用来说,可维护性比可复用性更重要。优先让变更集中在单一组件中。
CRP:共同复用原则
The Common Reuse Principle
不要强迫用户依赖他们不需要的东西
核心思想
这是 ISP(接口隔离原则)在组件层面的体现。组件中的类应该一起被复用——如果你只用其中一个类,就应该用所有的类。
反例:
组件 A 包含:
├── 日志工具类 ← 用户需要
├── 字符串工具类 ← 用户需要
├── 数据库连接池 ← 用户不需要
└── 邮件发送器 ← 用户不需要
问题:用户只想用工具类,却被迫依赖数据库和邮件相关的代码
→ 数据库连接池升级时,用户也要跟着升级、重新测试
正确做法:拆分成多个小组件,让用户只依赖真正需要的部分。
三原则的张力
这三个原则 互相矛盾,你无法同时完美满足:
| 原则 | 关注点 | 牺牲 |
|---|---|---|
| REP | 便于复用 | 可能引入不相关的类 |
| CCP | 便于维护 | 可能让组件变大 |
| CRP | 最小化依赖 | 可能导致变更分散 |
架构师的权衡
- 项目早期:侧重 CCP(可维护性),快速开发和迭代
- 项目成熟:逐渐侧重 REP(可复用性),方便其他项目复用
- 始终关注:CRP,避免引入不必要的依赖
速查表
| 原则 | 英文全称 | 一句话 | 对应 SOLID |
|---|---|---|---|
| REP | Reuse/Release Equivalence | 复用粒度 = 发布粒度 | - |
| CCP | Common Closure Principle | 一起变更的类放一起 | SRP |
| CRP | Common Reuse Principle | 不强迫依赖不需要的 | ISP |
延伸阅读
- 《Clean Architecture》第 13-14 章:组件内聚原则 & 组件耦合原则
- SOLID 原则:SRP、ISP 与组件原则的对应关系