rokevin
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
  • 模板方法模式(Template Method Pattern)

  • 一、定义
  • 二、意图
  • 三、结构
  • 四、优点
  • 五、缺点
  • 六、类图(Mermaid)
  • 七、时序图(Mermaid)
  • 八、适用环境
  • 九、模式分析
    • 1. 核心逻辑:“好莱坞原则”
    • 2. 钩子方法的作用
    • 3. 与策略模式的区别
  • 十、模式扩展
    • 1. 带钩子的模板方法(最常用)
    • 2. 抽象模板与具体模板的多层继承
    • 3. 模板方法的参数化
    • 4. 默认模板方法
  • 十一、模式应用
  • 十二、Android 中的应用
    • 1. Activity 的生命周期(间接体现)
    • 2. RecyclerView.Adapter
    • 3. AsyncTask(已废弃,原理参考)
    • 4. BaseAdapter(ListView 适配器)
  • 十三、代码实现(Java 版)
    • 1. 抽象类(定义模板方法和步骤)
    • 2. 具体子类(支付宝支付)
    • 3. 具体子类(微信支付)
    • 4. 客户端调用(测试)
    • 5. 输出结果
  • 十四、总结

模板方法模式(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)父类中定义的 “可选步骤”,默认空实现或简单逻辑,子类可通过重写钩子决定是否执行某分支(如 “是否需要发送短信通知”)。

四、优点

  1. 代码复用性高:将多个子类的公共流程(如日志、校验)提取到父类,避免重复编码。
  2. 算法结构稳定:模板方法用final修饰,确保子类无法修改算法的执行顺序,避免流程混乱。
  3. 扩展灵活:子类只需重写特定的抽象方法或钩子方法,即可实现个性化需求,符合 “开闭原则”(对扩展开放,对修改关闭)。
  4. 职责清晰:父类负责 “定义流程”,子类负责 “实现细节”,符合 “单一职责原则”。

五、缺点

  1. 子类数量可能增多:如果算法的可变步骤较多,或需要大量个性化实现,会导致子类数量激增,增加系统维护成本。
  2. 继承的局限性:依赖继承关系实现扩展,若父类修改(如新增抽象方法),所有子类都需同步修改,可能影响现有逻辑。
  3. 流程修改困难:若需调整算法的整体流程(如新增一个步骤),必须修改父类的模板方法,违反 “开闭原则”(对修改关闭)。

六、类图(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. 多个子类共享相同算法流程:如 “支付流程”“文件解析流程”“订单处理流程” 等,流程固定但细节不同。
  2. 需要固定算法执行顺序:如 “先登录→再验证权限→最后操作资源”,不允许子类打乱顺序。
  3. 需要通过子类扩展算法细节:如 “排序算法” 的骨架固定(比较→交换→循环),但比较逻辑(升序 / 降序)由子类实现。
  4. 需要控制子类的扩展范围:仅允许子类修改算法的特定步骤,不允许修改整体流程。

九、模式分析

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() { /* 默认通知逻辑 */ }
}

十一、模式应用

模板方法模式在框架和业务系统中应用广泛,典型场景包括:

  1. 框架底层设计:
    • Spring 的 JdbcTemplate:固定 “加载驱动→创建连接→执行 SQL→关闭连接” 的流程,子类只需实现RowMapper(结果映射);
    • Android 的 AsyncTask(已废弃,被Coroutine替代):固定 “主线程→子线程执行任务→主线程更新 UI” 的流程,子类重写doInBackground()和onPostExecute()。
  2. 业务流程标准化:
    • 电商订单处理:固定 “创建订单→支付→发货→确认收货→完成” 流程,子类实现 “支付”“发货” 的具体逻辑;
    • 报表生成:固定 “读取数据→处理数据→渲染报表→导出” 流程,子类实现 “数据处理” 和 “渲染报表”。
  3. 工具类封装:
    • 文件解析:固定 “打开文件→读取内容→解析→关闭文件” 流程,子类实现 “解析”(如 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 版)

以 “电商订单支付” 为场景,实现模板方法模式:

  1. 抽象类 AbstractOrderPayment:定义支付流程的模板方法和公共步骤;
  2. 具体子类 AlipayPayment、WechatPayment:实现支付宝和微信支付的细节;
  3. 钩子方法 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元
【微信支付】支付成功:已扣除微信零钱
【微信支付】发送通知:微信服务号推送支付成功消息
【公共步骤】记录支付日志:支付成功,状态已更新

十四、总结

模板方法模式是 “流程标准化” 与 “实现个性化” 的最佳实践,其核心价值在于:

  1. 固化流程:通过final模板方法确保算法顺序不可变,避免流程混乱;
  2. 复用代码:提取公共步骤到父类,减少子类重复编码;
  3. 灵活扩展:子类只需重写特定步骤,即可快速适配新场景(如新增 “银联支付”,只需新增子类实现doPayment())。

在实际开发中,模板方法模式常用于框架设计(如 Android 的Adapter、Spring 的JdbcTemplate)和业务流程封装(如订单、支付、报表)。需注意避免子类过度膨胀,若流程可变步骤过多,可结合 “策略模式” 进一步优化(如用组合替代继承,动态切换步骤实现)。

最近更新:: 2025/10/22 15:36
Contributors: 罗凯文, luokaiwen