8.1 设计模式误用复盘 本节摘要:设计模式的误用比不用更贵:为简历写的抽象成了每次变更的税。本节从三个真实翻车(单例的隐藏依赖、模板方法的僵化、四层包装的策略模式)讲起,给出"三次法则"的引入纪律、常用模式在 Java 生态的现代形态(很多已被语言与框架吸收),以及评审时识别过度设计的信号。 翻车一:单例的隐藏依赖网 某规则引擎把十几个服务做成了单例(自研的 getInstance 风格),两年后的测试成了灾难:任何一个类单测都要拉起整个单例网络—— 内部构造时拉起 ,后者又连了配置中心。想测一个纯计算函数?先起半个世界。 单例模式的本质问题不是"只有一个实例",而是把依赖关系从构造期(可见、可注入)转到了运行期(隐藏、全局)。Spring 的 IoC 容器(第 7.
本节摘要:设计模式的误用比不用更贵:为简历写的抽象成了每次变更的税。本节从三个真实翻车(单例的隐藏依赖、模板方法的僵化、四层包装的策略模式)讲起,给出"三次法则"的引入纪律、常用模式在 Java 生态的现代形态(很多已被语言与框架吸收),以及评审时识别过度设计的信号。
某规则引擎把十几个服务做成了单例(自研的 getInstance 风格),两年后的测试成了灾难:任何一个类单测都要拉起整个单例网络——RuleEngine.getInstance() 内部构造时拉起 ConfigService.getInstance(),后者又连了配置中心。想测一个纯计算函数?先起半个世界。
单例模式的本质问题不是"只有一个实例",而是把依赖关系从构造期(可见、可注入)转到了运行期(隐藏、全局)。Spring 的 IoC 容器(第 7.1 节)恰好治好了这个病:默认单例的作用域,但依赖全部显式注入,测试时换成 mock 即可。现代 Java 里手写单例的正当场景只剩下工具类与无状态纯函数集合(且静态方法更直白),有依赖的"单例"一律交给容器。
报表框架用模板方法定义骨架:父类里 generate() 固定了"取数-计算-渲染-上传"四步,子类覆写各步。三年后一个新需求只要"渲染+上传"(数据来自上游传入)——但模板的骨架动不了,子类被迫覆写"取数"为空操作、"计算"为透传。四个子类里两个是"空壳适配",看代码的人完全无法分辨哪些步骤是真实逻辑。
模板方法的软肋在第 2 章埋过伏笔:继承是强耦合,骨架一变全体震颤,而骨架无法预知所有未来变体。同样的需求用组合 + 函数式表达就灵活得多:
ReportBuilder.of(fetcher, renderer, uploader) // 步骤是注入的组件 .skipFetchingWhen(dataReady) // 变体是组合的选择 不是空覆写 .generate();
策略被提升为参数(Function/Consumer),骨架与变体的关系从继承变成装配——这正是 JDK 与 Spring 的演化方向:Comparator 的组合、Function 的链式、Spring 的 BeanPostProcessor 链,全是"模式被 API 化"的实例。
新项目技术负责人要求"体现设计功底",一个简单的折扣计算被包装成:Strategy 接口 → 抽象模板 → 四个具体策略 → 工厂 → 上下文——改一个折扣规则要跨五个文件。而业务上折扣规则总共三条,且半年没变过。评审时的灵魂拷问:这三条规则如果用三个 if 写,多少行?二十行。用模式写,多少行?两百行,外加一层所有人都得学的间接。

GoF 二十三个模式里,相当一部分在现代 Java 里有了更轻的原生形态,识别它们能避免"手写已被吸收的模式":
| 经典模式 | 现代形态 | 备注 |
|---|---|---|
| 单例 | 容器管理 Bean / enum | 手写仅限无依赖工具类 |
| 策略 | 函数式接口注入 | Lambda 即策略对象 |
| 模板方法 | 组合 + 函数参数 | 装配胜过继承 |
| 观察者 | 事件总线 / Stream / Reactor | Spring 事件最常用 |
| 建造者 | record + 紧凑构造 / Lombok | 不可变优先 |
| 装饰器 | java.io 的流家族是教材 | 新代码用组合包装 |
| 代理 | JDK 动态代理 / CGLib | 框架 AOP 的底 第3.3节 |
| 迭代器 | for-each / Stream | 语言内建 |
这不是"模式过时了",是模式的意图不变、实现载体升级了。真正该内化的是意图:策略是"变化点外置",观察者是"依赖倒置的通知版",装饰器是"不改原类加行为"。带着意图看新框架,处处是模式的转世。
把"该不该抽象"变成评审可执行的条款:
XxxHandler⚠️ 常见坑:把"用了多少模式"当设计评审的加分项。评审该问的是反方向——这个抽象删掉会怎样?如果答案是"代码更直白",删。
💡 关键直觉:模式是重复问题的命名解,不是简历的关键词。先有三次真实的疼,再引入对应的药——顺序反过来,药本身就是病。
下一节讲性能:怎么把"感觉慢"变成"定位到行"。