跳转至

组件设计原则

本文是《Clean Architecture》组件章节的读书笔记,探讨如何设计良好的组件边界。

什么是组件?

组件是部署的最小单元——它们是系统中可以独立部署的最小实体。

语言/平台 组件形式
Java .jar 文件
Ruby .gem 文件
.NET .dll 文件
编译型语言 二进制文件的聚合
解释型语言 源文件的聚合

组件的两种部署方式

  1. 链接成单一可执行文件:如 C++ 的静态链接
  2. 打包成归档文件:如 Java 的 .war
  3. 独立部署的插件:如动态加载的 .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 与组件原则的对应关系

评论