← 返回题目列表

模板方法模式有哪些角色?调用流程是什么?

高频 简单 第 1 / 26 题 更新于 2026/07/28
模板方法模式AbstractClassConcreteClass调用流程

简化版

模板方法模式主要有抽象父类和具体子类两个角色。抽象父类定义模板方法和步骤方法,具体子类实现可变步骤;调用时外部调用父类模板方法,模板方法按固定顺序调用各个步骤,部分步骤动态绑定到子类实现。

详细版

模板方法模式的角色通常很简单:

  1. AbstractClass:抽象父类,定义模板方法和公共步骤。
  2. ConcreteClass:具体子类,实现父类声明的抽象步骤。

调用流程是:

  1. 客户端调用模板方法;
  2. 模板方法执行父类固定步骤;
  3. 遇到抽象步骤时,通过多态调用子类实现;
  4. 遇到钩子方法时,根据子类覆盖结果决定是否扩展或跳过;
  5. 整个流程按父类规定的顺序完成。

模板方法模式的关键是“流程入口在父类,变化点在子类”。客户端通常不直接调用子类步骤,而是调用统一的模板方法。

完整版教学

一、抽象父类承担流程控制

抽象父类是模板方法模式的核心。它负责定义流程骨架:

public abstract class ReportExporter {
    public final void export() {
        queryData();
        checkPermission();
        generateFile();
        uploadFile();
        afterExport();
    }
}

这里的 export() 就是模板方法。它决定导出的步骤顺序。

父类可以实现公共步骤,例如查询数据、权限校验、上传文件;也可以声明抽象步骤,例如生成不同格式文件。

二、具体子类承担变化步骤

具体子类只实现可变步骤:

public class ExcelReportExporter extends ReportExporter {
    protected void generateFile() {
        // 生成 Excel 文件
    }
}

public class PdfReportExporter extends ReportExporter {
    protected void generateFile() {
        // 生成 PDF 文件
    }
}

子类不需要关心整个导出流程,只需要关心自己负责的步骤。

这能减少重复,也能避免子类随意改流程顺序。

三、模板方法调用依赖多态

模板方法在父类中定义,但执行到抽象步骤时,会动态调用子类实现。

例如:

ReportExporter exporter = new ExcelReportExporter();
exporter.export();

调用的是父类的 export(),但其中的 generateFile() 会执行 ExcelReportExporter 的实现。

这就是模板方法模式的底层机制:继承 + 多态。

四、步骤方法的访问权限要控制

步骤方法通常设计为 protected,不建议全部暴露为 public

原因是:外部调用方应该使用模板方法,而不是绕过流程直接调用某个步骤。

如果所有步骤都是 public,调用方可能直接调用 generateFile(),就破坏了模板方法固定流程的意义。

比较常见的设计是:

  • 模板方法:public final
  • 必须实现的步骤:protected abstract
  • 可选扩展点:protected
  • 内部公共步骤:privateprotected final

五、调用流程要避免子类反向控制父类

模板方法模式强调父类控制流程。子类如果频繁反过来调用父类内部方法、改变父类状态,结构就容易混乱。

比较好的边界是:

  • 父类编排步骤;
  • 子类实现步骤;
  • 子类不要绕过模板方法;
  • 父类不要知道具体子类。

这样继承关系才比较清晰。

六、父类调用子类的动态分派过程

模板依次调用 5 个步骤,其中 2 个是抽象步骤、1 个是钩子;运行 ConcreteTask 时,这 3 个调用点会按实际子类动态分派。

ConcreteTask.run -> AbstractTask.template -> fixed -> overridden step -> hook

这里真正需要保护的是父类掌握的流程不变量:客户端只能进入模板入口,子类只能改写约定好的步骤。评审时应逐步标出哪些行为固定、哪些行为必须实现、哪些行为只是可选钩子,避免父类和子类互相猜测。

七、继承边界与流程契约验证

检查维度应确认的内容
机制正确性这是“父类调用子类”的好莱坞原则,不是客户端逐步指挥子类。
适用边界抽象类控制时序,子类提供变化;若子类能任意跳过关键步骤,父类就失去了保护流程不变量的作用。
测试证据记录步骤进入与退出序列,覆盖默认钩子、覆写钩子、步骤异常和清理路径
工程代价重点评估父类改动会影响多少子类、protected 扩展面有多大,以及新增变化是否迫使无关子类一起修改

易错点:这是“父类调用子类”的好莱坞原则,不是客户端逐步指挥子类。

八、常见误区与追问

  • 误区:模板方法必须由客户端逐个调用步骤。 客户端只调用模板入口,步骤编排由抽象父类控制,否则算法骨架会泄露。
  • 误区:模板方法要求所有步骤都由子类覆写。 稳定步骤应在父类直接实现;只有必要变化点才做抽象步骤,可选变化点才做有默认实现的钩子。
  • 追问:步骤方法为什么通常用 protected? 它允许子类实现扩展点,又避免客户端绕过模板入口直接破坏流程。
  • 追问:模板方法为什么常声明为 final? 为了防止子类改写算法骨架和步骤顺序;若框架有意允许重写,则必须明确不变量和扩展契约。
  • 追问:模板方法最关键的回归测试是什么? 是父类契约测试:所有子类都必须满足固定步骤顺序、公共前后置行为和异常清理规则。

九、加强记忆

模板方法模式的角色很简单:抽象父类定流程,具体子类填步骤。调用时外部调用模板方法,模板方法按固定顺序执行,遇到变化点通过多态调用子类实现。