模板方法模式(Template Method Pattern)
模板方法模式是行为型设计模式的核心模式之一,其核心思想是 “定义算法骨架,延迟具体步骤到子类”。它通过在父类中固定算法的执行流程,将流程中易变的步骤交给子类实现,从而实现 “流程复用” 与 “具体实现灵活扩展” 的平衡。
一、定义
模板方法模式(Template Method Pattern)定义一个操作中的算法骨架,将算法的一些步骤延迟到子类中实现。父类模板方法确保算法的结构保持不变,同时允许子类重定义算法的特定步骤,而无需修改算法的整体流程。
简单来说:父类定流程,子类填细节。
二、意图
- 固定算法的执行顺序,避免子类随意修改流程逻辑(如 “先验证→再处理→最后记录日志” 的流程不可打乱)。
- 提取多个子类的公共代码到父类,减少代码重复(如流程中的通用步骤,如日志记录、参数校验)。
- 让子类专注于实现算法中的个性化步骤,符合 “单一职责原则”。
三、结构
模板方法模式包含 5 个核心角色,各角色职责明确,协作完成 “流程固定 + 细节扩展” 的目标:
| 角色名称 | 核心职责 |
|---|---|
| 抽象类(Abstract Class) | 1. 定义模板方法(Template Method):固定算法的执行流程,调用各步骤方法; 2. 定义基本方法:包括 “抽象方法”(子类必须实现)和 “具体方法”(父类提供默认实现,子类可重写); 3. 定义钩子方法(Hook Method):可选步骤,子类可通过重写钩子控制流程分支。 |
| 具体子类(Concrete Class) | 1. 实现抽象类中的 “抽象方法”,提供算法特定步骤的具体逻辑; 2. (可选)重写父类的 “具体方法” 或 “钩子方法”,定制流程细节。 |
| 模板方法(Template Method) | 抽象类中的核心方法,通常用final修饰(防止子类重写打乱流程),按顺序调用各基本方法。 |
| 基本方法(Primitive Method) | - 抽象方法:子类必须实现的步骤(如 “计算折扣”); - 具体方法:父类提供默认实现,子类可按需重写(如 “生成订单号”)。 |
| 钩子方法(Hook Method) | 父类中定义的 “可选步骤”,默认空实现或简单逻辑,子类可通过重写钩子决定是否执行某分支(如 “是否需要发送短信通知”)。 |
四、优点
- 代码复用性高:将多个子类的公共流程(如日志、校验)提取到父类,避免重复编码。
- 算法结构稳定:模板方法用
final修饰,确保子类无法修改算法的执行顺序,避免流程混乱。 - 扩展灵活:子类只需重写特定的抽象方法或钩子方法,即可实现个性化需求,符合 “开闭原则”(对扩展开放,对修改关闭)。
- 职责清晰:父类负责 “定义流程”,子类负责 “实现细节”,符合 “单一职责原则”。
五、缺点
- 子类数量可能增多:如果算法的可变步骤较多,或需要大量个性化实现,会导致子类数量激增,增加系统维护成本。
- 继承的局限性:依赖继承关系实现扩展,若父类修改(如新增抽象方法),所有子类都需同步修改,可能影响现有逻辑。
- 流程修改困难:若需调整算法的整体流程(如新增一个步骤),必须修改父类的模板方法,违反 “开闭原则”(对修改关闭)。
六、类图(Mermaid)

以下类图以 “电商订单支付流程” 为例,抽象类OrderPayment定义模板方法processPayment()(固定流程:校验→支付→通知→记录),子类AlipayPayment和WechatPayment分别实现支付宝和微信支付的细节:
classDiagram
direction TB
class AbstractOrderPayment {
+final processPayment(): void // 模板方法(固定流程)
-validateOrder(): void // 具体方法(公共步骤:校验订单)
#abstract doPayment(): void // 抽象方法(子类实现:执行支付)
#abstract sendNotification(): void // 抽象方法(子类实现:发送通知)
-hookNeedReceipt(): boolean // 钩子方法(可选:是否需要发票)
-generateReceipt(): void // 具体方法(依赖钩子:生成发票)
-logPaymentResult(): void // 具体方法(公共步骤:记录日志)
}
class AlipayPayment {
+doPayment(): void // 实现支付宝支付
+sendNotification(): void // 实现支付宝支付通知
-hookNeedReceipt(): boolean // 重写钩子:是否需要发票
}
class WechatPayment {
+doPayment(): void // 实现微信支付
+sendNotification(): void // 实现微信支付通知
}
AbstractOrderPayment <|-- AlipayPayment
AbstractOrderPayment <|-- WechatPayment
七、时序图(Mermaid)
以下时序图展示 “用户使用支付宝支付订单” 的流程,体现模板方法如何调用各步骤:
sequenceDiagram
participant Client(用户)
participant AlipayPayment(支付宝支付子类)
participant AbstractOrderPayment(抽象父类)
Client->>AlipayPayment: 调用 processPayment()
AlipayPayment->>AbstractOrderPayment: 执行父类模板方法
AbstractOrderPayment->>AbstractOrderPayment: 1. validateOrder()(校验订单)
AbstractOrderPayment->>AlipayPayment: 2. doPayment()(支付宝执行支付)
AbstractOrderPayment->>AlipayPayment: 3. sendNotification()(支付宝发送通知)
AbstractOrderPayment->>AlipayPayment: 4. hookNeedReceipt()(判断是否要发票)
alt 需要发票(钩子返回true)
AbstractOrderPayment->>AbstractOrderPayment: 5. generateReceipt()(生成发票)
end
AbstractOrderPayment->>AbstractOrderPayment: 6. logPaymentResult()(记录日志)
AbstractOrderPayment->>Client: 返回支付结果
八、适用环境
当系统满足以下场景时,优先考虑模板方法模式:
- 多个子类共享相同算法流程:如 “支付流程”“文件解析流程”“订单处理流程” 等,流程固定但细节不同。
- 需要固定算法执行顺序:如 “先登录→再验证权限→最后操作资源”,不允许子类打乱顺序。
- 需要通过子类扩展算法细节:如 “排序算法” 的骨架固定(比较→交换→循环),但比较逻辑(升序 / 降序)由子类实现。
- 需要控制子类的扩展范围:仅允许子类修改算法的特定步骤,不允许修改整体流程。
九、模式分析
1. 核心逻辑:“好莱坞原则”
模板方法模式遵循 “好莱坞原则”(Don’t Call Us, We’ll Call You):子类无需主动调用父类,而是由父类模板方法在合适的时机调用子类的实现。
- 子类只需要 “被动等待” 父类调用其重写的方法,无需关心流程顺序;
- 父类完全掌控算法流程,避免子类 “越权” 修改流程。
2. 钩子方法的作用
钩子方法是模板方法模式的 “灵活开关”,主要用途:
- 控制流程分支:如
hookNeedReceipt()返回true时生成发票,返回false时跳过; - 扩展默认行为:父类钩子默认空实现,子类可重写钩子添加额外逻辑(如支付成功后额外记录用户积分);
- 适配特殊场景:某些子类无需执行某步骤时,可通过钩子跳过(如 “测试环境不发送真实短信”)。
3. 与策略模式的区别
模板方法模式和策略模式都用于 “封装变化”,但核心差异在于流程是否固定:
| 对比维度 | 模板方法模式 | 策略模式 |
|---|---|---|
| 核心思想 | 固定流程,扩展细节 | 封装不同算法,流程可替换 |
| 实现方式 | 继承(父类定流程,子类填细节) | 组合(上下文持有策略接口,动态切换) |
| 流程控制权 | 父类掌控流程 | 上下文或客户端掌控流程 |
| 适用场景 | 流程固定,细节可变 | 算法完全独立,需动态切换 |
十、模式扩展
1. 带钩子的模板方法(最常用)
如前文例子,通过钩子方法控制流程分支,是模板方法模式的标准扩展,平衡了 “流程固定” 和 “灵活扩展”。
2. 抽象模板与具体模板的多层继承
当多个子类共享部分细节时,可增加 “中间抽象类”,进一步提取共性:
- 例:
AbstractOrderPayment(顶层抽象类)→OnlinePayment(中间抽象类,实现 “在线支付” 的公共细节,如 “调用第三方接口”)→AlipayPayment/WechatPayment(具体子类,实现各自支付逻辑)。
3. 模板方法的参数化
允许模板方法接收参数,动态决定调用哪些步骤:
// 父类模板方法参数化
public final void processPayment(boolean needReceipt) {
validateOrder();
doPayment();
sendNotification();
if (needReceipt) { // 参数控制分支,替代钩子方法
generateReceipt();
}
logPaymentResult();
}
4. 默认模板方法
父类提供 “默认完整实现”,子类仅在需要时重写特定步骤(类似 “适配器模式” 的缺省适配器):
// 父类提供默认实现
public abstract class AbstractOrder {
public void process() {
validate(); // 默认校验
handle(); // 抽象方法,子类必须实现
notifyUser(); // 默认通知(子类可重写)
}
protected void validate() { /* 默认校验逻辑 */ }
protected abstract void handle();
protected void notifyUser() { /* 默认通知逻辑 */ }
}
十一、模式应用
模板方法模式在框架和业务系统中应用广泛,典型场景包括:
- 框架底层设计:
- Spring 的
JdbcTemplate:固定 “加载驱动→创建连接→执行 SQL→关闭连接” 的流程,子类只需实现RowMapper(结果映射); - Android 的
AsyncTask(已废弃,被Coroutine替代):固定 “主线程→子线程执行任务→主线程更新 UI” 的流程,子类重写doInBackground()和onPostExecute()。
- Spring 的
- 业务流程标准化:
- 电商订单处理:固定 “创建订单→支付→发货→确认收货→完成” 流程,子类实现 “支付”“发货” 的具体逻辑;
- 报表生成:固定 “读取数据→处理数据→渲染报表→导出” 流程,子类实现 “数据处理” 和 “渲染报表”。
- 工具类封装:
- 文件解析:固定 “打开文件→读取内容→解析→关闭文件” 流程,子类实现 “解析”(如 XML 解析、JSON 解析);
- 网络请求:固定 “创建请求→发送请求→接收响应→解析响应” 流程,子类实现 “请求参数构建” 和 “响应解析”。
十二、Android 中的应用
模板方法模式是 Android 框架设计的核心模式之一,以下是典型场景:
1. Activity 的生命周期(间接体现)
Activity 的生命周期方法(onCreate()、onStart()、onResume() 等)由系统(父类逻辑)按固定顺序调用,开发者(子类)只需重写特定生命周期方法实现业务逻辑,无需关心流程顺序。
- 系统框架:负责 “调用生命周期方法” 的模板逻辑;
- 开发者子类:重写
onCreate()初始化 UI、onPause()保存数据,即 “实现细节步骤”。
2. RecyclerView.Adapter
RecyclerView.Adapter 定义了 “创建视图→绑定数据” 的核心流程,开发者需重写抽象方法:
- 模板流程:
getItemCount()→onCreateViewHolder()→onBindViewHolder()(由RecyclerView内部调用); - 子类实现:重写
onCreateViewHolder()创建View,重写onBindViewHolder()绑定数据,无需关心RecyclerView的复用逻辑和流程顺序。
3. AsyncTask(已废弃,原理参考)
AsyncTask 固定 “主线程准备→子线程执行任务→主线程更新结果” 的流程:
- 模板方法:
execute()(固定调用onPreExecute()→doInBackground()→onPostExecute()); - 子类实现:重写
doInBackground()(子线程执行耗时任务)、onPostExecute()(主线程更新 UI),钩子方法onProgressUpdate()(可选更新进度)。
4. BaseAdapter(ListView 适配器)
与 RecyclerView.Adapter 类似,BaseAdapter 定义了 “获取数据数量→创建视图→绑定数据” 的流程,开发者需重写 getCount()、getView() 等方法,框架负责调用流程。
十三、代码实现(Java 版)
以 “电商订单支付” 为场景,实现模板方法模式:
- 抽象类
AbstractOrderPayment:定义支付流程的模板方法和公共步骤; - 具体子类
AlipayPayment、WechatPayment:实现支付宝和微信支付的细节; - 钩子方法
hookNeedReceipt():控制是否生成发票。
1. 抽象类(定义模板方法和步骤)
/**
* 抽象类:订单支付模板(定义支付流程骨架)
*/
public abstract class AbstractOrderPayment {
// 模板方法:固定支付流程(用final修饰,防止子类重写)
public final void processPayment() {
// 步骤1:校验订单(公共步骤,父类实现)
validateOrder();
// 步骤2:执行支付(个性化步骤,子类实现)
doPayment();
// 步骤3:发送支付通知(个性化步骤,子类实现)
sendNotification();
// 步骤4:判断是否需要生成发票(钩子方法控制分支)
if (hookNeedReceipt()) {
generateReceipt();
}
// 步骤5:记录支付日志(公共步骤,父类实现)
logPaymentResult();
}
/**
* 具体方法:校验订单(公共步骤,父类提供默认实现)
*/
private void validateOrder() {
System.out.println("【公共步骤】校验订单:订单号合法,金额不为0");
}
/**
* 抽象方法:执行支付(子类必须实现)
*/
protected abstract void doPayment();
/**
* 抽象方法:发送支付通知(子类必须实现)
*/
protected abstract void sendNotification();
/**
* 钩子方法:是否需要生成发票(默认不需要,子类可重写)
*/
protected boolean hookNeedReceipt() {
return false; // 默认不生成发票
}
/**
* 具体方法:生成发票(依赖钩子方法,父类实现)
*/
private void generateReceipt() {
System.out.println("【公共步骤】生成电子发票:已发送至用户邮箱");
}
/**
* 具体方法:记录支付日志(公共步骤,父类实现)
*/
private void logPaymentResult() {
System.out.println("【公共步骤】记录支付日志:支付成功,状态已更新\n");
}
}
2. 具体子类(支付宝支付)
/**
* 具体子类:支付宝支付(实现个性化步骤)
*/
public class AlipayPayment extends AbstractOrderPayment {
private String orderId;
private double amount;
public AlipayPayment(String orderId, double amount) {
this.orderId = orderId;
this.amount = amount;
}
// 实现抽象方法:支付宝支付逻辑
@Override
protected void doPayment() {
System.out.println("【支付宝支付】调用支付宝SDK:订单号=" + orderId + ",金额=" + amount + "元");
System.out.println("【支付宝支付】支付成功:已扣除支付宝余额");
}
// 实现抽象方法:支付宝通知逻辑
@Override
protected void sendNotification() {
System.out.println("【支付宝支付】发送通知:支付宝App推送支付成功消息");
}
// 重写钩子方法:支付宝订单默认需要生成发票
@Override
protected boolean hookNeedReceipt() {
return true;
}
}
3. 具体子类(微信支付)
/**
* 具体子类:微信支付(实现个性化步骤)
*/
public class WechatPayment extends AbstractOrderPayment {
private String orderId;
private double amount;
public WechatPayment(String orderId, double amount) {
this.orderId = orderId;
this.amount = amount;
}
// 实现抽象方法:微信支付逻辑
@Override
protected void doPayment() {
System.out.println("【微信支付】调用微信支付SDK:订单号=" + orderId + ",金额=" + amount + "元");
System.out.println("【微信支付】支付成功:已扣除微信零钱");
}
// 实现抽象方法:微信通知逻辑
@Override
protected void sendNotification() {
System.out.println("【微信支付】发送通知:微信服务号推送支付成功消息");
}
// 不重写钩子方法:使用父类默认值(不生成发票)
}
4. 客户端调用(测试)
/**
* 客户端:模拟用户选择支付方式
*/
public class OrderPaymentClient {
public static void main(String[] args) {
// 1. 支付宝支付订单
AbstractOrderPayment alipay = new AlipayPayment("ORDER_20240501_001", 99.9);
System.out.println("=== 支付宝支付流程 ===");
alipay.processPayment();
// 2. 微信支付订单
AbstractOrderPayment wechatPay = new WechatPayment("ORDER_20240501_002", 199.0);
System.out.println("=== 微信支付流程 ===");
wechatPay.processPayment();
}
}
5. 输出结果
=== 支付宝支付流程 ===
【公共步骤】校验订单:订单号合法,金额不为0
【支付宝支付】调用支付宝SDK:订单号=ORDER_20240501_001,金额=99.9元
【支付宝支付】支付成功:已扣除支付宝余额
【支付宝支付】发送通知:支付宝App推送支付成功消息
【公共步骤】生成电子发票:已发送至用户邮箱
【公共步骤】记录支付日志:支付成功,状态已更新
=== 微信支付流程 ===
【公共步骤】校验订单:订单号合法,金额不为0
【微信支付】调用微信支付SDK:订单号=ORDER_20240501_002,金额=199.0元
【微信支付】支付成功:已扣除微信零钱
【微信支付】发送通知:微信服务号推送支付成功消息
【公共步骤】记录支付日志:支付成功,状态已更新
十四、总结
模板方法模式是 “流程标准化” 与 “实现个性化” 的最佳实践,其核心价值在于:
- 固化流程:通过
final模板方法确保算法顺序不可变,避免流程混乱; - 复用代码:提取公共步骤到父类,减少子类重复编码;
- 灵活扩展:子类只需重写特定步骤,即可快速适配新场景(如新增 “银联支付”,只需新增子类实现
doPayment())。
在实际开发中,模板方法模式常用于框架设计(如 Android 的Adapter、Spring 的JdbcTemplate)和业务流程封装(如订单、支付、报表)。需注意避免子类过度膨胀,若流程可变步骤过多,可结合 “策略模式” 进一步优化(如用组合替代继承,动态切换步骤实现)。