设计模式详解——模板方法模式

设计模式详解------模板方法模式

一、模式概述

1. 模式定义

模板方法模式(Template Method Pattern) 是一种行为型设计模式

它定义一个操作中的算法骨架(固定流程) ,将某些步骤延迟到子类中实现,使得子类可以不改变算法结构,即可重新定义算法的某些特定步骤。

简单理解:

  • 父类定流程骨架
  • 子类填具体实现
  • 流程不变,实现可变

2. 核心思想

  • 父类提供固定模板方法,规定执行顺序与步骤
  • 抽象方法交给子类实现
  • 复用公共逻辑,避免代码重复
  • 符合开闭原则 + 单一职责

3. 主要角色

  1. 抽象模板类(AbstractClass)
    • 定义模板方法(final,防止子类重写流程)
    • 定义抽象方法(子类必须实现)
    • 提供通用方法(公共逻辑)
  2. 具体子类(ConcreteClass)
    • 实现父类的抽象方法
    • 完成步骤的具体逻辑
  3. 客户端(Client)
    • 使用子类对象,调用模板方法

二、适用场景

  1. 多个子类有共同流程,但部分步骤实现不同
  2. 希望统一控制流程,只开放部分步骤扩展
  3. 复杂算法抽取公共部分,减少重复代码
  4. 框架设计中,用于规定扩展点(钩子方法)

典型业务场景:

  • 订单创建流程(验参 → 查库存 → 计算金额 → 落库 → 通知)
  • 数据同步流程(拉取 → 解析 → 转换 → 入库)
  • 游戏开局流程(加载 → 初始化 → 开始 → 结算)
  • Spring、MyBatis 等框架大量使用模板模式

三、实战代码实现(模拟订单流程)

场景说明

不同类型订单(普通订单、秒杀订单)流程一致,但部分逻辑不同:

  1. 校验参数(通用)
  2. 处理库存(不同)
  3. 计算价格(不同)
  4. 创建订单(通用)
  5. 发送通知(通用)

1. 抽象模板类(定义流程骨架)

java 复制代码
/**
 * 抽象订单模板类
 */
public abstract class AbstractOrderService {

    /**
     * 模板方法:固定下单流程
     * final 防止子类篡改流程
     */
    public final void createOrder() {
        checkParam();       // 1. 参数校验(通用)
        handleStock();      // 2. 库存处理(子类实现)
        calculatePrice();   // 3. 价格计算(子类实现)
        saveOrder();        // 4. 保存订单(通用)
        sendNotify();       // 5. 发送通知(通用)
    }

    // 通用方法:父类实现
    protected void checkParam() {
        System.out.println("参数校验通过");
    }

    // 抽象方法:子类必须实现
    protected abstract void handleStock();
    protected abstract void calculatePrice();

    // 通用方法
    protected void saveOrder() {
        System.out.println("订单已保存到数据库");
    }

    protected void sendNotify() {
        System.out.println("下单成功通知已发送");
    }
}

2. 具体子类:普通订单

java 复制代码
/**
 * 普通订单实现
 */
public class NormalOrderService extends AbstractOrderService {

    @Override
    protected void handleStock() {
        System.out.println("普通商品:锁定库存");
    }

    @Override
    protected void calculatePrice() {
        System.out.println("普通价格计算:原价");
    }
}

3. 具体子类:秒杀订单

java 复制代码
/**
 * 秒杀订单实现
 */
public class SeckillOrderService extends AbstractOrderService {

    @Override
    protected void handleStock() {
        System.out.println("秒杀商品:Redis预扣库存 + 异步扣减");
    }

    @Override
    protected void calculatePrice() {
        System.out.println("秒杀价格计算:活动价 + 优惠叠加");
    }
}

4. 客户端调用

java 复制代码
public class Client {
    public static void main(String[] args) {
        System.out.println("===== 普通订单 =====");
        AbstractOrderService normal = new NormalOrderService();
        normal.createOrder();

        System.out.println("\n===== 秒杀订单 =====");
        AbstractOrderService seckill = new SeckillOrderService();
        seckill.createOrder();
    }
}

5. 运行结果

复制代码
===== 普通订单 =====
参数校验通过
普通商品:锁定库存
普通价格计算:原价
订单已保存到数据库
下单成功通知已发送

===== 秒杀订单 =====
参数校验通过
秒杀商品:Redis预扣库存 + 异步扣减
秒杀价格计算:活动价 + 优惠叠加
订单已保存到数据库
下单成功通知已发送

四、钩子方法(Hook)扩展

钩子方法让子类可选性影响流程,增强灵活性。

在抽象类中添加:

java 复制代码
// 钩子方法:默认返回true,子类可覆盖
protected boolean isNeedNotify() {
    return true;
}

修改模板方法:

java 复制代码
public final void createOrder() {
    checkParam();
    handleStock();
    calculatePrice();
    saveOrder();
    // 根据钩子决定是否执行
    if (isNeedNotify()) {
        sendNotify();
    }
}

子类可以重写钩子方法关闭通知:

java 复制代码
@Override
protected boolean isNeedNotify() {
    return false;
}

五、模板方法模式优缺点

优点

  1. 代码复用:公共逻辑统一在父类
  2. 流程可控:父类固定流程,子类无法破坏结构
  3. 扩展方便:新增实现只需加子类,符合开闭原则
  4. 便于维护:统一规范,减少重复逻辑

缺点

  1. 类数量可能增多(每个实现一个子类)
  2. 父类若设计不合理,子类实现会很复杂
  3. 流程固定,过度复杂的流程不适合

六、经典框架应用(面试高频)

1. Spring JdbcTemplate

  • execute 固定流程:获取连接 → 执行语句 → 释放资源
  • 子类/回调实现 SQL 逻辑

2. MyBatis BaseExecutor

  • 数据库操作流程固定,不同执行器(Simple/Reuse/Batch)只替换部分逻辑

3. HttpServlet

  • service() 方法分发 doGet/doPost,是典型模板方法

4. Spring 生命周期

Bean 初始化流程:实例化 → 依赖注入 → 初始化 → 销毁


七、模板方法 VS 策略模式(面试必问)

对比项 模板方法模式 策略模式
核心思想 父类定流程,子类填步骤 一组可互换算法,自由切换
实现方式 继承 组合/接口
控制力度 流程固定,不可修改 整个算法可替换
耦合度 较高(继承) 低(组合优于继承)

一句话区分:

  • 模板方法:流程不变,步骤变
  • 策略模式:算法整体互换

八、总结

  1. 模板方法 = 流程骨架 + 可变步骤
  2. 父类用 final 固定模板方法,保证流程不被破坏
  3. 抽象方法交给子类实现,公共方法父类统一实现
  4. 适合流程固定、实现多变的业务场景
  5. 框架底层大量使用,是代码复用与规范扩展的利器

一句话记忆:父类定骨架,子类填细节;流程不改动,扩展更轻松!


相关推荐
Lyyaoo.2 小时前
【JAVA基础面经】JAVA中的泛型
java
自然常数e2 小时前
预处理讲解
java·linux·c语言·前端·visual studio
无籽西瓜a2 小时前
【西瓜带你学设计模式 | 第四期 - 抽象工厂模式】抽象工厂模式 —— 定义、核心结构、实战示例、优缺点与适用场景及模式区别
java·后端·设计模式·软件工程·抽象工厂模式
always_TT2 小时前
内存泄漏是什么?如何避免?
android·java·开发语言
白鸽梦游指南2 小时前
docker仓库的工作原理及搭建仓库
java·docker·eureka
※DX3906※2 小时前
SpringBoot之旅4: MyBatis 操作数据库(进阶) 动态SQL+MyBatis-Plus实战,从入门到熟练,再也不踩绑定异常、SQL拼接坑
java·数据库·spring boot·spring·java-ee·maven·mybatis
java1234_小锋2 小时前
Java高频面试题:怎么实现Redis的高可用?
java·开发语言·redis
jiankeljx2 小时前
MySQL-mysql zip安装包配置教程
java
FlagOS智算系统软件栈2 小时前
智源×Eclipse基金会携手打造PanEval,中欧协同开启“评测+开源+合规”新模式
java·eclipse·开源