3445 字
约 11 分钟
6
S18-X01 · 23 种设计模式速查表

适用读者:评审/面试前 10 分钟要过一遍的人;写代码时"想不起那个模式叫什么"的人。
前置:S18-J01–J04(判据与机制)。
覆盖旧文:A-2379(第二、三份导出 761 点)、A-2392(第二、三份导出 267 点)——合计 1,028 点;其中 appendix 606 点、duplicate_ref 733 点中的模式部分。
扩展点:本篇不新增 E 点,交叉引用 E-S18-003/004/005/006/007。
上一篇:S18-A04 · 高可用分布式架构设计 + 从需求到架构的评审清单
下一篇:S18-X02 · DDD 术语与建模模板速查。


1. 怎么用这张表

四列的分工是固定的,别混着读:

  • 意图:这个模式"想把什么变化关起来"——选型看这一列;
  • 场景:真实工程里长什么样——判断"我是不是遇到它了";
  • 反例:什么情况下不该用它——这一列比前两列更值钱;
  • 易混淆:最容易和谁搞混,以及一句话区分。

读法建议:先看"反例"列。如果反例命中你的现状,直接跳过这个模式;只有反例不命中、场景命中时,才回去看正文的实现细节。


2. 创建型(5)

模式 意图 场景 反例(别用) 易混淆
单例 Singleton 保证一个 ClassLoader 内只有一个实例 配置快照、ID 生成器、连接池门面 想用它藏全局可变状态;分布式"唯一" 与 Spring 单例 Bean(每容器一个)不同
工厂方法 Factory Method 让子类决定实例化哪个类 同一接口多种实现,且实现集合会增长 只有两个分支且一年没变 简单工厂(不是 GoF 23 种)、抽象工厂(产品族)
抽象工厂 Abstract Factory 一次性替换一整套相关产品 多数据库方言的 DAO 全家桶、多云的存储适配 只有一条产品线 工厂方法只造一个产品
建造者 Builder 把"构造参数"与"构造过程"分开 参数 >5、有必填、需校验、要不可变 参数 ≤4 且无校验(用 record 就够) 与"工厂"区别:工厂选类型,建造者拼参数
原型 Prototype 复制一个已配置好的对象 活动模板复制、复杂配置克隆 需要逐字段重置业务键(那不是复制,是新建) 与拷贝构造器:后者更安全(编译器查漏字段)

3. 结构型(7)

模式 意图 场景 反例(别用) 易混淆
适配器 Adapter 把一个接口翻译成另一个 接第三方 SDK、兼容遗留接口 两边都能改(那就统一接口) 代理/装饰都要求接口不变
桥接 Bridge 把两个维度的继承改成"持有" JDBC Driver/Connection、多渲染引擎 × 多平台 只有一个变化维度 与策略:桥接是结构维度,策略是行为维度
组合 Composite 树形结构,叶子与容器同接口 菜单树、组织架构、规则树 树只有两层(用 List 即可) 与装饰:组合是"部分-整体",装饰是"同接口叠加"
装饰 Decorator 同接口上叠加行为,可任意组合 I/O 流、缓存/压缩/加密管线 只加一种能力(直接改原类) 与代理:装饰加能力,代理控访问
门面 Facade 给一组子系统一个简化入口 支付门面(下单+扣款+发券) 门面里出现业务分支(会变上帝类) 与中介者:门面单向简化,中介者多方协调
享元 Flyweight 共享不可变内在状态,省内存 字符样式、棋盘、大量重复值对象 状态可变还共享(线程安全灾难) 与对象池:享元省内存,池化省创建
代理 Proxy 控制对目标的访问 懒加载、鉴权、远程 stub、AOP 只是想要日志(用切面更省) 与装饰:见上;与适配器:接口不变

4. 行为型(11)

模式 意图 场景 反例(别用) 易混淆
责任链 Chain of Responsibility 让请求沿链传递,直到被处理 校验链、风控规则、审批流 规则 >10 条且每条都查库 与拦截器:责任链多判断,拦截器多改写
命令 Command 把"请求"变成可存储/排队/撤销的对象 任务队列、事务日志、撤销栈 只是把 if-else 换个名字 与策略:命令带"执行时机",策略只带算法
解释器 Interpreter 用类表达一套文法 规则引擎内部、DSL 自造表达式语言(用 SpEL/Aviator) 与访问者:都遍历 AST,职责不同
迭代器 Iterator 统一遍历,不暴露内部结构 自定义集合 业务代码里手写遍历器(用 Stream) 与组合:迭代器管"逐个访问",组合管"整体结构"
中介者 Mediator 把 N×N 通信收敛成星形 消息总线、UI 组件协调 中介者里出现业务规则 与门面:中介者双向,门面单向
备忘录 Memento 保存/恢复对象状态 编辑器撤销、流程回滚 状态巨大(快照成本爆炸) 与命令:命令负责"做什么",备忘录负责"存到哪"
观察者 Observer 一对多广播 Spring ApplicationEvent、事件总线 需要顺序/事务保证(用 MQ) 与中介者:观察者是广播,中介者是路由
状态 State 行为随内部状态改变 订单状态机、连接状态机 状态 ≤2 且无非法迁移约束 与策略:状态由内部迁移,策略由外部选择
策略 Strategy 同一动作的算法可替换 支付渠道、折扣计算、排序策略 只有 2 个分支 与状态:见上;与工厂:工厂造对象,策略换算法
模板方法 Template Method 骨架固定,步骤延迟到子类 导入/导出、批处理、生命周期回调 步骤顺序本身会变 与策略:模板用继承,策略用组合
访问者 Visitor 不改类的前提下加操作(双分派) AST 遍历、报表导出多种格式 类型集合开放(用 sealed + switch 更好) 与迭代器:访问者管"做什么",迭代器管"怎么走"

5. 与 S04 / S18-J02–J04 的重复关系

本文的两份原始导出(A-2379 第二/三份、A-2392 第二/三份)里,有大量"模式定义 + 优缺点 + 代码示例"的八股段。它们的归宿是 duplicate_ref,理由如下:

重复内容 主讲位置 本表处理
单例的五种写法与线程安全细节 S18-J02 §2(含反射实测) 只留一行"场景/反例"
工厂三兄弟的结构与代码 S18-J02 §3(含注册表实测) 只留"意图/易混淆"
代理三种实现(静态/JDK/CGLIB) S18-J03 §2 只留"场景/反例"
装饰叠加顺序的代价 S18-J03 §3(含计数实测) 只留"易混淆"
责任链的顺序/终止/异常 S18-J04 §2(含输出实测) 只留"反例"
模板方法的钩子与好莱坞原则 S18-J04 §3 只留"意图"
Java 语言层面的接口/泛型/反射 S04(Java 语言与核心 API) 不重复,按链接指回

这也是本表"不写代码"的原因:代码已经在正文,速查表再抄一遍就是二次真相——同一段代码存在两处,早晚会有一处过时。



5.5 症状反查表(不知道用什么时,从症状找)

你现在的症状 候选模式 先排除
一个方法里 8 个 if (type),每次加类型都要改 策略 / 注册表工厂 分支 ≤3 → 留着
加功能要改老代码,改完还要回归全量 装饰 / 策略 只是加日志 → 切面
每个实体都带一堆 setXxx,状态组合非法 状态 / 充血实体 状态 ≤2 → 枚举 + 校验
一个操作要在 6 个地方各做一次 观察者 / 领域事件 需要顺序 → MQ
调用外部接口,对方一改版就崩 适配器 + ACL 对方可控 → 统一接口
同一段流程复制了 4 份,只有中间一步不同 模板方法 步骤会变 → 责任链
对象构造参数 12 个,还有必填 建造者 无校验 → record
全局状态散落各处,测试互相污染 依赖注入(不是模式,是容器) 别用单例掩盖

用法:先按"症状"找候选,再用 §1 的"反例"列排除,最后回正文看实现。三步之内还定不下来,说明变化点没找对——回到 S18-J01 §3 的五问。

5.6 面试口径:同一件事的两种说法

你原来的说法(八股) 换成这个说法(有判断)
"我用策略模式解决了 if-else" "支付渠道从 3 个涨到 7 个,每次加渠道要改同一个 switch,所以拆成注册表 + 策略,新增只加一个类"
"单例保证全局唯一" "配置快照用容器单例,业务对象每次新建;分布式唯一性靠 Redis 锁,不靠单例"
"装饰器让代码更灵活" "缓存层可拆可装,且必须装在指标的内层,否则统计口径会变成请求数而不是回源数"
"观察者模式解耦" "用事务提交后的事件 + 消费端幂等;事务内同步发会在回滚后仍然通知,我们踩过"
"责任链实现审批流" "链上每段只做判断不查库,数据一次性批量取;超过 6 段就改成规则引擎"

一句话:面试官要听的是"变更点 + 代价 + 你怎么验证",不是模式名字。


6. 三条速查判据(贴在显示器上那种)

  1. 接口变了吗? 变了 → 适配器;没变 → 继续。
  2. 是在加能力,还是在控访问? 加能力 → 装饰;控访问 → 代理。
  3. 变化由谁触发? 外部选择 → 策略;内部迁移 → 状态;要排队/撤销 → 命令。

其余三种情况(流程、通知、遍历):

  1. 流程固定个别步骤不同 → 模板方法;流程不固定、要按序试 → 责任链。
  2. 一对多广播 → 观察者;多对多协调 → 中介者。
  3. 遍历/操作/快照 → 迭代器/访问者/备忘录,且优先看 Java 17 的语言特性是否已经够用。

6.5 查表时的四个误区

误区 真相 怎么避免
按"模式名"找答案,先定名字再找场景 顺序反了:先定变化点,再找模式 用 §5.5 的症状反查表,从症状进入
看到"反例"列写"别用"就以为永远不能用 反例是当前条件下不划算,条件变了结论就变 每次问"分支数/变化频率变了吗"
把"易混淆"当成"二选一" 很多场景要组合(如代理 + 装饰 + 责任链) 先拆成"谁判断/谁控访问/谁加能力"三段
表里有 23 种,就想凑满 一个项目常用 8–10 种,剩下的只需读得懂 以"改动扩散面"衡量收益,不以覆盖模式数为目标

7. 自测(30 秒版)

  1. "给所有 DAO 方法加计时"该用哪个?为什么不是装饰?
  2. "新增一种导出格式"用工厂还是策略?两者的边界在哪?
  3. 只有两个支付渠道,你会抽象吗?说出判据。
  4. 为什么 Cloneable 被 Effective Java 建议避免?
  5. 观察者与 MQ 的分界线是什么?

下一篇

S18-X02 · DDD 术语与建模模板速查

模式查完了,下一篇查 DDD:40 个术语的一句话定义 + 出处小节、事件风暴便签模板、聚合清单模板、上下文映射表模板,以及三张需要登记的素材异常。


本篇属于系列《S18 · 架构方法与领域建模》。素材来源:A-2379/A-2392 的第二、三份导出(appendix 606 点 / duplicate_ref 733 点);定义与代码主讲在 S18-J02–J04,Java 语言基础指回 S04。

S18-X01 · 23 种设计模式速查表
https://www.clxhxhhr.top/posts/5448/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。