设计模式 16:创建决策何时交给子类
原先所有开单请求都生成纸质收据 paper:200。新增电子收据后,金额必须大于零的校验不能因为新增渠道被绕过。把渠道字符串放进同一个创建方法是一种修复,但若不同开单流程分别负责自己的产品,谁能扩展创建决策而不改共同流程?
不先造工厂,先保住旧约束
labs/16 的 Before.issue 校验正金额后直接生成纸质文本。这没有缺陷,直到电子渠道出现。After 的基类用 final issue 固定调用顺序:检查金额 → 调用 createReceipt → 取得文本。Paper 与 Electronic 子类仅决定产品;测试确认新 email:200、旧 paper:200,并断言两种子类对 0 都抛异常。它们是教学中的 Creator 和 ConcreteCreator;产品是返回 text() 的 Receipt。本地字符串收据不表示真实打印或发邮件。
1 | |
继承让子类控制被固定流程使用的创建钩子,这才是此处 GoF Factory Method 的意图,不是因为方法名称有 factory。Alternative.of(kind, cents) 是静态方法,其中 switch 选择纸质或电子收据,同时属于常说的 Simple Factory 风格:调用者传入品种,单一方法选择具体产品。Java 的静态工厂方法只指“由静态方法返回实例”这一构造接口形式,并不自动表示 GoF Factory Method;它也可以根本没有分支或可扩展层次。不能把三者写成同义词。
| 路径 | 谁选择收据 | 0 分与未知品种 |
|---|---|---|
Before |
单一方法内写死纸质 | 0 分拒绝;无电子入口 |
After |
Paper / Electronic 子类覆写钩子 |
基类先拒绝 0 分 |
Alternative |
调用者传 kind,静态方法中 switch |
0 分、未知品种都拒绝 |
若只有一个品种,一个 new 就够;若品种固定且在一个入口选择,Alternative 少两个 Creator 子类。只有当多个独立流程需要改变产品同时复用校验/执行骨架时,继承的变化边界才值得支付。After 的新子类若返回 null,基类仍会失败;示例未把插件质量或外部副作用封进类型系统。
flowchart LR
Caller[调用方] --> Creator[After.issue]
Paper[Paper 子类] -.继承.-> Creator
Electronic[Electronic 子类] -.继承.-> Creator
Creator --> Hook[createReceipt 钩子]
Hook --> Product[Receipt]
验证与练习
在 examples/design-patterns/ 运行 ./mvnw -B -ntp -pl labs/16 -am test 及 ./mvnw -B -ntp verify,实际输入、环境、退出码和原始输出见 examples/design-patterns/evidence/16/RUN.md。不是用打印结果代替断言;同一旧合同、电子新增合同和边界均有测试。没有测试并发、磁盘输出或邮件交付。
- 新增短信收据产品
sms:200,先为旧纸质、电子与新增文本及 0 分拒绝补断言,再分别修改子类与静态工厂,记录谁需要修改共同issue。 - 如果最终只有纸质一种品种,删去工厂钩子写回直接构造;运行旧合同,解释何时不应保留空的子类层次。
参考资料
- GoF 原书公开图书馆 PDF,3.3 Factory Method,目录页标注起始页 121:https://cpcc.chd.gov.in/Content/PDFs/w8UkV3tWNyEtsbUZSWJ7fVhuB9A3tsGYdm4w6VGzg2wUTNFYikqnvvFbbkiW2zmfspPEghd7QTamiMby3lVIBemrhdVWwt6rOQnm.pdf 。
- GoF 模式分类总览,InfoWorld/JavaWorld:https://www.infoworld.com/article/2170730/design-patterns-the-big-picture-part-1-design-pattern-history-and-classification.html 。
- 本篇来源边界:
writing-plans/design-patterns/SOURCES.md。 - Java SE 21 JLS 方法分派章节:https://docs.oracle.com/javase/specs/jls/se21/html/jls-15.html 。
- 实验代码:
examples/design-patterns/labs/16/。
上一节:15 阅读模式先看变化;下一节:17 两种产品如何成套。





