23种设计模式的分与合:先拆开,再组装
上一篇把 23 个模式做成了选型地图,这一篇换一个更本质的视角:设计模式没有创造新语法,它做的核心动作只有两个——把耦合拆开,把职责合起来。掌握这条主线,比背住 23 个类图更管用。
一、先建立两个动作
所有设计模式都可以被拆成两个动作。
1. 分:把变化从稳定代码里拿走
一段代码之所以越来越难改,通常不是因为功能多,而是因为多个变化原因挤在同一个类或同一个方法里。
“分”就是把它们拆开:
- 把创建逻辑从业务逻辑中分出去;
- 把算法从调用方中分出去;
- 把状态判断从各个方法中分出去;
- 把访问控制从真实业务中分出去;
- 把多个对象之间的网状通信拆成单向通信。
2. 合:用抽象把分散的角色重新组织起来
拆开之后,如果只是把一个大类变成一堆互相引用的小类,代码并不会自动变好。模式的另一半价值,在于用一个稳定的抽象把它们重新组合起来。
“合”的常见手段有:
- 统一接口;
- 统一入口;
- 统一容器或上下文;
- 统一消息总线或协调者;
- 统一对象结构或树形结构。
所以设计模式的完整动作通常是:先分后合,分是为了隔离变化,合是为了保持稳定协作。
二、23个模式各自的“分”与“合”
下面这张表把每个模式都拆成“先分了什么”和“又合成了什么”。你会发现,很少有模式只做其中一件事。
| 序号 | 类型 | 模式 | 先分:把什么拆出去 | 再合:用什么把角色重新组织 |
|---|---|---|---|---|
| 1 | 创建型 | 单例模式 | 把实例创建与业务使用分开 | 用全局唯一入口收敛所有访问 |
| 2 | 创建型 | 工厂模式 | 把“创建谁”从业务调用中分出 | 用工厂接口或注册表统一创建入口 |
| 3 | 创建型 | 抽象工厂模式 | 把成套产品的创建逻辑拆成产品族 | 用抽象工厂约束一组对象必须配套 |
| 4 | 创建型 | 建造者模式 | 把构造步骤从目标对象中拆出 | 用 Builder 链把分散步骤合成为完整对象 |
| 5 | 创建型 | 原型模式 | 把“从零创建”替换成“复制已有对象” | 用统一 clone 语义复用一个可靠样板 |
| 6 | 结构型 | 适配器模式 | 把内部接口与外部 SDK 的直接绑定分开 | 用翻译层把新旧接口接合 |
| 7 | 结构型 | 桥接模式 | 把抽象与实现两个变化维度拆开 | 用组合关系把它们重新连接 |
| 8 | 结构型 | 组合模式 | 把叶子与容器的处理逻辑从客户端分走 | 用统一节点接口合成树形结构 |
| 9 | 结构型 | 装饰器模式 | 把横切能力从核心逻辑中拆出 | 用同接口的包装链叠加能力 |
| 10 | 结构型 | 外观模式 | 把客户端与多个子系统的耦合拆开 | 用单一门面收敛复杂协调过程 |
| 11 | 结构型 | 享元模式 | 把可变状态与不变状态分开 | 用共享池把重复对象合成共享样本 |
| 12 | 结构型 | 代理模式 | 把访问控制从真实业务中拆出 | 用同接口代理统一承接访问 |
| 13 | 行为型 | 模板方法模式 | 把可变步骤从固定流程中分出 | 用抽象模板把流程骨架固定下来 |
| 14 | 行为型 | 策略模式 | 把算法分支从业务判断中拆出 | 用上下文与策略接口统一选择和执行 |
| 15 | 行为型 | 观察者模式 | 把通知接收方从被观察者中拆出 | 用订阅关系合成一对多通知 |
| 16 | 行为型 | 责任链模式 | 把层层 if-else 拆成独立处理节点 | 用链式指针合成一条处理路径 |
| 17 | 行为型 | 命令模式 | 把调用者与接收者拆开 | 用命令对象把动作、排队和撤销统一起来 |
| 18 | 行为型 | 解释器模式 | 把规则从硬编码中拆出 | 用表达式对象树合成语法解释过程 |
| 19 | 行为型 | 迭代器模式 | 把遍历方式从容器内部拆出 | 用统一迭代器接口合成遍历过程 |
| 20 | 行为型 | 中介者模式 | 把对象之间的网状依赖拆开 | 用中介者把通信合成为星型结构 |
| 21 | 行为型 | 备忘录模式 | 把状态快照从对象外部操作中拆出 | 用备忘录对象统一保存和恢复状态 |
| 22 | 行为型 | 状态模式 | 把状态判断从每个方法中拆出 | 用状态对象合成对象的状态机 |
| 23 | 行为型 | 访问者模式 | 把新增操作从稳定元素中拆出 | 用访问者接口统一承载一组操作 |
三、分与合不是对立,而是同一枚硬币的两面
很多人会把“拆分”和“整合”理解成相反方向,但设计模式里的分与合通常是连续发生的。
1. 策略模式:先分算法,再合选择
满减、折扣、新人价本来是一个大 if-else。策略模式先把它拆成多个策略类,然后引入 DiscountContext 把“策略查找、持有、执行”重新合起来。
如果没有第二步的“合”,客户端就要自己知道所有策略类,拆得越细越难用。
2. 组合模式:先分叶子与容器,再合成统一节点
文件系统中有文件也有目录。如果客户端分别处理 File 和 Directory,代码会出现大量类型判断。组合模式先承认叶子与容器不同,再用统一的 Component 接口把它们合起来,让客户端只看到“节点”。
3. 中介者模式:先拆网状依赖,再合成中心协调
库存、支付、物流、积分、通知互相直接调用时,关系是一张网。中介者模式先切断参与者之间的直接依赖,再把它们的通信全部合到 Mediator 上。拆的是连接,合的是协作规则。
4. 访问者模式:先分操作,再合到访问者接口
文本、图片、表格的数据结构很稳定,但统计、导出、预览这些操作不断新增。访问者模式先让元素不再自己实现所有操作,再把同一批操作合到一个访问者对象里。分的是“操作责任”,合的是“操作入口”。
四、真实系统里,模式往往需要组合使用
单个模式解决单个问题,但一个业务流程往往同时需要“分”和“合”多次。以下组合在工程中最常见。
1. 工厂 + 策略 + 模板方法
工厂负责把策略对象创建出来,策略负责选择算法,模板方法负责固定整个流程骨架。
比如订单结算:
- 工厂:根据优惠类型创建策略;
- 策略:替换满减、折扣、会员价;
- 模板方法:固定“校验订单 → 计算金额 → 记录流水”的流程。
这是一个很典型的“先分算法、再合入口、最后固定流程”的组合。
2. 观察者 + 中介者
观察者适合“一对多通知”,但当参与者之间还有复杂的先后顺序时,观察者容易变成隐式依赖网。
更稳妥的用法是:
- 观察者负责广播“发生了什么”;
- 中介者负责编排“接下来谁来做”。
前者解决通知解耦,后者解决流程集中。
3. 组合 + 迭代器 + 访问者
树形结构里,组合负责统一节点,迭代器负责统一遍历,访问者负责把统计、导出等操作从节点中拆出。
例如报表树:
- 组合:让文本、图片、表格都有统一的
accept; - 迭代器:让客户端不关心树的内部存储方式;
- 访问者:新增导出、统计时不再修改节点类。
4. 建造者 + 工厂
建造者解决“复杂对象怎么一步步创建”,工厂解决“应该选择哪个建造者或产品”。一个负责过程的组织,一个负责入口的收敛,经常配合使用。
五、用“分与合”检查一段代码
做代码评审时,可以用四个问题替代“这里该用什么模式”:
- 哪里最容易变? 这个变化现在被写在什么类或什么方法里?
- 是否已经把变化拆出去了? 如果新增一种算法、状态、渠道或操作,是否需要改动稳定代码?
- 拆完之后有没有一个稳定接口把角色合起来? 如果拆成一堆互相了解具体类的小类,说明只有“分”,没有“合”。
- 这个“合”会不会成为新的上帝对象? 门面、中介者、上下文如果承载太多,就是过度收敛,需要再次分。
小结
23 种设计模式看似各有套路,本质都在做同一件事:把容易变化的部分分出去,把必须稳定协作的部分合起来。
只分不合,系统会碎成高耦合的小类;只合不分,系统会退化成巨大的上帝类。真正的设计能力,是在“什么时候该分、分到哪一层”和“什么时候该合、用什么抽象来合”之间做判断。

593

被折叠的 条评论
为什么被折叠?



