模板方法模式和工厂方法模式有什么关系?
简化版
模板方法模式关注固定算法流程,工厂方法模式关注把对象创建延迟到子类。两者都可能使用继承和抽象方法,但意图不同:模板方法让子类填流程步骤,工厂方法让子类决定创建哪种对象。
详细版
模板方法模式和工厂方法模式容易被放在一起问,因为它们都常见于框架设计,也都可能出现父类调用子类方法。
模板方法模式:
- 父类定义业务流程;
- 子类实现某些流程步骤;
- 关注行为流程复用。
工厂方法模式:
- 父类定义创建对象的抽象方法;
- 子类决定具体创建哪种产品;
- 关注对象创建解耦。
它们也可以组合:模板方法的某个步骤可能是工厂方法。例如父类固定处理流程,但某一步需要创建不同处理器,就把创建动作交给子类工厂方法。
完整版教学
一、两者共同点是都可能依赖继承
模板方法模式和工厂方法模式都可能长成这样:
抽象父类
-> 抽象方法
-> 子类实现抽象方法
但设计模式不能只看类图。它们的区别在意图。
模板方法解决“流程如何复用”,工厂方法解决“对象如何创建”。
二、模板方法关注流程
模板方法模式中的抽象方法通常表示流程中的一个步骤。
public final void execute() {
prepare();
doExecute();
finish();
}
protected abstract void doExecute();
这里 doExecute() 是业务步骤。父类关心的是整个执行流程。
三、工厂方法关注创建
工厂方法模式中的抽象方法通常负责创建产品对象。
public abstract class Dialog {
public void render() {
Button button = createButton();
button.render();
}
protected abstract Button createButton();
}
这里 createButton() 的重点是创建对象,而不是完成业务流程中的某个算法步骤。
子类决定创建 WindowsButton 还是 WebButton。
四、两者可以自然组合
很多框架里会同时出现两种模式。
例如父类模板方法固定流程:
创建处理器 -> 初始化 -> 执行 -> 销毁
其中“创建处理器”这一步可以交给子类:
protected abstract Handler createHandler();
这个抽象方法既是模板流程中的一步,也承担工厂方法的创建职责。
所以它们不是互斥关系,而是关注点不同。
五、面试回答要抓“意图”
如果面试官问两者区别,不要只回答“一个行为型,一个创建型”。这只是分类。
更好的回答是:
- 模板方法模式把固定流程放父类,让子类实现变化步骤;
- 工厂方法模式把对象创建放到子类,避免父类依赖具体产品;
- 两者都可以通过抽象方法扩展,但一个扩展行为步骤,一个扩展创建对象。
六、流程扩展点与对象创建扩展点
文档处理模板有 4 个步骤,其中 createParser() 返回 PDF 或 CSV 解析器;前者是模板骨架,后者是其中 1 个工厂方法。
template.process -> createParser(factory method) -> parse -> validate -> save
这里真正需要保护的是父类掌握的流程不变量:客户端只能进入模板入口,子类只能改写约定好的步骤。评审时应逐步标出哪些行为固定、哪些行为必须实现、哪些行为只是可选钩子,避免父类和子类互相猜测。
七、继承边界与流程契约验证
| 检查维度 | 应确认的内容 |
|---|---|
| 机制正确性 | 看到父类和子类不能直接下结论,必须找到扩展点到底是行为步骤还是对象创建。 |
| 适用边界 | 模板方法解决“步骤顺序怎样固定”,工厂方法解决“具体产品由谁创建”;它们经常组合而非互斥。 |
| 测试证据 | 对两种实现运行同一父类契约测试,验证固定步骤顺序一致,再分别验证各自变化步骤 |
| 工程代价 | 重点评估父类改动会影响多少子类、protected 扩展面有多大,以及新增变化是否迫使无关子类一起修改 |
易错点:看到父类和子类不能直接下结论,必须找到扩展点到底是行为步骤还是对象创建。
八、常见误区与追问
- 误区:两者都用继承,所以本质相同。 代码结构相似不等于意图相同,一个组织算法流程,一个延迟产品创建。
- 误区:模板方法要求所有步骤都由子类覆写。 稳定步骤应在父类直接实现;只有必要变化点才做抽象步骤,可选变化点才做有默认实现的钩子。
- 追问:工厂方法必须出现在模板方法里吗? 不必须;它可以独立被客户端或其他流程调用,组合只是常见用法。
- 追问:模板方法为什么常声明为 final? 为了防止子类改写算法骨架和步骤顺序;若框架有意允许重写,则必须明确不变量和扩展契约。
- 追问:模板方法最关键的回归测试是什么? 是父类契约测试:所有子类都必须满足固定步骤顺序、公共前后置行为和异常清理规则。
九、加强记忆
模板方法和工厂方法都可能用继承,但目的不同。模板方法问的是“流程怎么走”,工厂方法问的是“对象谁来造”;它们可以组合,工厂方法也可以成为模板流程中的一个步骤。