设计模式的本质,是在「变化」与「稳定」之间划界,让系统对扩展开放、对修改关闭。
几乎所有模式都在回答同一个问题:哪一部分将来最可能变?怎样让「变」不影响「不变」?
建议的学习顺序
#mermaid-svg-zM7CTIIAlhZlHRxo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zM7CTIIAlhZlHRxo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zM7CTIIAlhZlHRxo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zM7CTIIAlhZlHRxo .error-icon{fill:#552222;}#mermaid-svg-zM7CTIIAlhZlHRxo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zM7CTIIAlhZlHRxo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zM7CTIIAlhZlHRxo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zM7CTIIAlhZlHRxo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zM7CTIIAlhZlHRxo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zM7CTIIAlhZlHRxo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zM7CTIIAlhZlHRxo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zM7CTIIAlhZlHRxo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zM7CTIIAlhZlHRxo .marker.cross{stroke:#333333;}#mermaid-svg-zM7CTIIAlhZlHRxo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zM7CTIIAlhZlHRxo p{margin:0;}#mermaid-svg-zM7CTIIAlhZlHRxo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zM7CTIIAlhZlHRxo .cluster-label text{fill:#333;}#mermaid-svg-zM7CTIIAlhZlHRxo .cluster-label span{color:#333;}#mermaid-svg-zM7CTIIAlhZlHRxo .cluster-label span p{background-color:transparent;}#mermaid-svg-zM7CTIIAlhZlHRxo .label text,#mermaid-svg-zM7CTIIAlhZlHRxo span{fill:#333;color:#333;}#mermaid-svg-zM7CTIIAlhZlHRxo .node rect,#mermaid-svg-zM7CTIIAlhZlHRxo .node circle,#mermaid-svg-zM7CTIIAlhZlHRxo .node ellipse,#mermaid-svg-zM7CTIIAlhZlHRxo .node polygon,#mermaid-svg-zM7CTIIAlhZlHRxo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zM7CTIIAlhZlHRxo .rough-node .label text,#mermaid-svg-zM7CTIIAlhZlHRxo .node .label text,#mermaid-svg-zM7CTIIAlhZlHRxo .image-shape .label,#mermaid-svg-zM7CTIIAlhZlHRxo .icon-shape .label{text-anchor:middle;}#mermaid-svg-zM7CTIIAlhZlHRxo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zM7CTIIAlhZlHRxo .rough-node .label,#mermaid-svg-zM7CTIIAlhZlHRxo .node .label,#mermaid-svg-zM7CTIIAlhZlHRxo .image-shape .label,#mermaid-svg-zM7CTIIAlhZlHRxo .icon-shape .label{text-align:center;}#mermaid-svg-zM7CTIIAlhZlHRxo .node.clickable{cursor:pointer;}#mermaid-svg-zM7CTIIAlhZlHRxo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zM7CTIIAlhZlHRxo .arrowheadPath{fill:#333333;}#mermaid-svg-zM7CTIIAlhZlHRxo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zM7CTIIAlhZlHRxo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zM7CTIIAlhZlHRxo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zM7CTIIAlhZlHRxo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zM7CTIIAlhZlHRxo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zM7CTIIAlhZlHRxo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zM7CTIIAlhZlHRxo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zM7CTIIAlhZlHRxo .cluster text{fill:#333;}#mermaid-svg-zM7CTIIAlhZlHRxo .cluster span{color:#333;}#mermaid-svg-zM7CTIIAlhZlHRxo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zM7CTIIAlhZlHRxo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zM7CTIIAlhZlHRxo rect.text{fill:none;stroke-width:0;}#mermaid-svg-zM7CTIIAlhZlHRxo .icon-shape,#mermaid-svg-zM7CTIIAlhZlHRxo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zM7CTIIAlhZlHRxo .icon-shape p,#mermaid-svg-zM7CTIIAlhZlHRxo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zM7CTIIAlhZlHRxo .icon-shape .label rect,#mermaid-svg-zM7CTIIAlhZlHRxo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zM7CTIIAlhZlHRxo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zM7CTIIAlhZlHRxo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zM7CTIIAlhZlHRxo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 设计原则
几种高频模式
读框架源码
自己项目小步重构
易混模式对照
高频模式建议优先:观察者、策略、工厂、组合、命令、装饰 。
它们覆盖了日常 80% 的「变化点」问题。其余模式在碰到具体问题时再查,完全来得及。
1. 模式不是语法,是「变化点管理术」
设计模式既不是库,也不是框架,更不是某种 C++ 语法糖。它是在反复出现的场景里,类与对象如何分工协作的命名经验。
| 模式 | 隔离的变化点 |
|---|---|
| 策略 | 可互换的算法 |
| 观察者 | 谁会响应状态变化 |
| 工厂 | 具体类型如何被创建 |
| 桥接 | 抽象与实现两个维度 |
| 装饰 | 动态叠加的职责 |
| 命令 | 请求的发起与执行 |
读模式时,先问「这里的变化点是什么」,比背 UML 更有用。
2. 三条底层逻辑
GoF 23 种模式背后,反复出现三条主线:
2.1 依赖抽象,不依赖具体
高层模块不应绑死在 new FileLogger() 上,而应依赖 Logger 接口。换实现时,调用方不动------依赖倒置(DIP),策略、工厂、桥接的共同地基。
2.2 组合优于继承
继承是白盒复用,子类与父类紧耦合。Has-A(持有一个策略、包一层装饰)是黑盒复用,往往更灵活。策略、装饰、桥接都在践行合成复用(CRP)。
2.3 分离「做什么」与「谁来做 / 何时做」
| 分离什么 | 典型模式 |
|---|---|
| 算法 vs 使用算法的上下文 | 策略 |
| 请求 vs 执行者 | 命令 |
| 通知 vs 响应 | 观察者 |
| 构造 vs 使用 | 工厂、建造者 |
| 树形结构 vs 遍历方式 | 组合 + 迭代器 |
本质都是降低耦合、让职责更单一(SRP)。
先原则,再模式。
3. 模式从「坏味道」反推,不是从空白纸画 UML
模式几乎总是重构的目标形状,而不是项目启动时的第一张架构图:
text
满屏 if-else → 策略 / 状态
类越来越大 → 单一职责 / 门面
继承树爆炸 → 桥接 / 组合 / 装饰
对象互相引用成网 → 中介者 / 观察者
创建逻辑散落各处 → 工厂 / 原型
撤销重做难做 → 命令 + 备忘录
会嗅坏味道,比会背 23 个名字更接近本质。
4. 模式是「角色关系」,不是固定类名
GoF 描述的是角色:Context、Strategy、ConcreteStrategy;Subject、Observer......
真实代码里可以叫 Checkout、PaymentStrategy、Alipay------角色不变,命名随意。
同一系统里常多个模式叠加:
- 命令 + 备忘录 → 撤销栈
- 组合 + 迭代器 → 遍历文档树
- 工厂 + 单件 → 全局配置管理
- 策略 + 模板方法 → 框架定流程、应用填步骤
模式的本质是可复用的微型架构片段。
5. 三类模式:管三件不同的事
| 类型 | 本质问题 | 一句话 |
|---|---|---|
| 创建型 | 对象怎么来 | 把 new 从业务里挪走,隐藏构造复杂度 |
| 结构型 | 类怎么拼 | 用组合、包装、适配组织更大结构 |
| 行为型 | 职责怎么分、怎么通信 | 算法、状态、通知、请求如何流动 |
创建型管诞生 ,结构型管形状 ,行为型管动作与协作------这是对复杂度的三种切分。
6. 模式有代价:用复杂换灵活
每种模式都带来:
- 更多的类与接口
- 更多的间接层
- 更高的理解成本
因此模式的另一面是:有意识地接受一定复杂度,换取未来的可扩展性 。
简单问题用简单解法;模式不是勋章,是在确认真有变化压力时的投资。
7. 模式 vs 框架 vs 语言
| 设计模式 | 框架(Qt / VTK) | 现代 C++ | |
|---|---|---|---|
| 是什么 | 设计经验的名字 | 已实现的大结构 | 语言级抽象 |
| 例子 | 策略、观察者 | 信号槽、vtkInteractorStyle |
std::function、RAII |
| 关系 | 思想层 | 模式已嵌入 API | 可简化实现,思想仍在 |
- STL 迭代器 = 迭代器模式的标准化
std::function= 轻量策略 / 命令- Qt 信号槽 = 观察者思想的框架化
vtkCommand= Observer 回调载体(名字像 Command,语义不是 GoF 命令,见 VTK 专题)
本质在思想;形态随语言与框架演进。
重点与注意(速记)
重点 :模式 = 变化点隔离 + 依赖抽象 + 组合复用 的命名结构。
重点 :从坏味道出发重构,比一上来「模式驱动设计」更务实。
注意 :模式是角色关系,可叠加;不是每个类只能戴一顶帽子。
注意:框架里已内置大量模式;读 API 也是在读模式落地。