模板方法不必是final但强烈建议声明为final,以固化算法流程;应使用public final void execute()调用protected钩子或abstract方法,避免子类覆盖破坏模式意图。

模板方法必须是 final 的吗
不是必须,但强烈建议声明为 final。模板方法的核心职责是固化算法流程,如果允许子类重写,就等于放开了骨架控制权,直接破坏模式意图。Java 中常见写法是:public final void execute(),内部调用一系列 protected 的钩子或抽象方法。
容易踩的坑:
- 把模板方法写成
protected或public非final,导致子类意外覆盖流程 - 在模板方法里调用
public方法而非protected,暴露了不该被外部直接触发的步骤 - 忘记将抽象步骤声明为
abstract或提供默认空实现,导致子类遗漏关键扩展点
抽象方法 vs 钩子方法怎么选
两者都用于延迟实现,但语义和强制性不同。抽象方法(abstract)表示“子类必须实现”,钩子方法(protected void beforeStep() { })表示“子类可选重写”。
使用场景:
- 核心步骤不可跳过(如数据库事务开启、参数校验、结果封装)→ 用
abstract方法 - 辅助行为可能需要定制(如日志埋点、缓存清理、异步通知)→ 用带空体的
protected钩子方法 - 钩子方法若被误判为必需,子类可能漏掉重写,导致逻辑静默失效;而抽象方法编译期就会报错,更安全
为什么不能在模板方法里 new 具体子类
模板方法定义在抽象父类中,它的存在意义就是解耦流程与具体实现。一旦在 execute() 里写 new ConcreteA(),就等于把算法骨架和某个实现强绑定了,彻底违背“子类扩展”的前提。
正确做法是:由客户端决定实例化哪个子类,然后调用其继承来的模板方法。例如:
new ExportToCsv().execute(); new ExportToJson().execute();
性能与兼容性影响:
- 硬编码 new 会阻碍 Spring 等容器管理生命周期,也难以做 AOP 增强
- 测试时无法 mock 具体实现,只能走真实逻辑,单元测试成本陡增
- 后续新增导出格式(如 Excel)时,还得改父类代码,违反开闭原则
子类里调用 super 模板方法是否合理
不合理,也不必要。模板方法本就是为子类统一入口设计的,子类应该只负责实现抽象/钩子方法,而不是反过来驱动父类流程。如果子类主动调用 super.execute(),说明它把自己当成了中间协调者,架构角色错位。
真正需要的是明确分层:
- 父类:只管流程顺序、异常兜底、公共资源获取(如连接池、上下文)
- 子类:只管“怎么做”,不操心“什么时候做”或“要不要做”
- 客户端:负责构造子类 + 调用模板方法,是唯一触发点
最容易被忽略的一点:模板方法的入参设计。如果所有子类都需要相同参数(如 Request request),应统一收口到模板方法签名里;若部分子类不需要,说明这个参数不属于骨架层级,该拆到具体方法中传入。










