YC Yc.W home Systems · C++ · AI Infrastructure
Computer Science · Deep-dive

设计模式实践指南:按变化点选择,而不是按名字套用

把创建型、结构型和行为型模式整理成变化点地图,帮助 C++ 后端和架构代码做出更清晰的取舍。

Curated guide deep dive Featured Editorial review
Explore all Computer Science notes →
Related topics C & C++ Programming

设计模式的价值不在于“项目里出现了多少个模式名”,而在于是否把一个变化点隔离到了正确的边界。模式是沟通设计和权衡取舍的语言,不是必须复用的类模板。

本文把站内逐篇模式笔记重新编排为选择指南;每篇历史笔记仍保留其原始代码和上下文。

先定位变化点

变化点 常用模式 典型问题
对象如何创建 Factory Method、Abstract Factory、Builder、Prototype 创建过程复杂、类型组合多、需要复用已有对象
结构如何变化 Adapter、Bridge、Composite、Decorator、Facade、Proxy 接口不兼容、层次爆炸、需要在不修改核心对象的情况下叠加能力
行为如何变化 Strategy、State、Observer、Command、Chain of Responsibility、Iterator、Visitor 规则经常切换、状态转移复杂、事件生产者与消费者耦合
流程如何固定 Template Method 整体步骤稳定,但局部步骤需要扩展
复杂协作如何隔离 Mediator、Memento 对象之间互相通知,或需要保存/恢复状态

五个最值得先掌握的模式

Strategy:替换算法

把可变化的算法抽成同一接口,让调用方只依赖“能力”而不是具体分支。适合压缩、排序、校验、路由等规则经常变化的场景。代价是策略数量增加后需要管理注册和选择逻辑。

State:让状态自己负责转移

如果代码里出现大量 if (state == ...),可以考虑把状态相关行为放回状态对象。关键不是消除所有条件,而是让每个状态拥有清晰的合法转移和拒绝行为。

Adapter:隔离外部变化

适配器适合连接第三方库、旧接口和领域模型。优先把“外部数据如何转换”为边界问题,避免让外部类型扩散到业务层;转换失败要有可观察的错误,而不是静默返回默认值。

Observer:解耦事件生产与处理

当一个状态变化需要通知多个关注者时,Observer 能减少直接依赖。但要明确订阅顺序、重复通知、失败处理和生命周期;否则“解耦”可能只是把复杂度藏到事件总线里。

Composite:统一树形对象和叶子对象

树、菜单、场景图和权限层级都适合用 Composite 思考。它减少客户端对层级的分支,但前提是叶子与组合节点确实拥有一致的公共协议,否则不要为了“统一”而隐藏本质差异。

不应该忽略的反模式

  • 只有一个实现且没有变化点,却先搭完整工厂体系。
  • 策略接口泄漏了所有实现细节,导致调用方仍然知道具体类型。
  • 事件总线没有所有权、顺序和失败语义。
  • 装饰器层层包装后,调试日志看不出真实调用链。
  • 为模仿教材而增加间接层,却没有降低耦合或提高测试性。

可以用一个问题检验设计是否值得保留:

如果删掉这个抽象,变化会扩散到多少个模块?如果抽象长期不变,它是否真的提供了隔离?

适合 C++ 后端的设计习惯

  • 用组合表达运行时变化,用模板表达编译期稳定的多态选择。
  • 让接口围绕调用方需要的能力设计,而不是照搬实现类。
  • 为工厂、缓存、连接池和事件处理器明确生命周期与失败回滚。
  • 将日志、指标和测试替身作为依赖设计的一部分。
  • 先写清晰的直接实现,再根据真实的第二实现或第三种变化抽象。

复习顺序

  1. 先读 设计模式总览,建立分类和意图。
  2. 用 Strategy、State、Observer、Adapter、Facade 五个模式做小型重构练习。
  3. 再读创建型和结构性模式,比较它们对对象数量、接口数量和依赖方向的影响。
  4. 最后学习访问者、备忘录、中介者和享元,重点讨论它们适用的规模与代价。
  5. 每学一个模式,都写下一个“不使用它”的版本,说明选择依据。

真正成熟的代码不是模式最多,而是模式出现的地方恰好对应真实变化,且抽象的成本有清晰的收益。

Source notes / 资料来源

本文是对站内历史资料的重新编排。原始文章保持独立发布,下面列出本文重组所依据的来源;正文中的引用会直接指向对应的编号来源。标签和署名信息描述仓库中的来源状态,不等同于外部事实验证。