模板方法模式:定义算法骨架的设计模式

模板方法模式:定义算法骨架的设计模式

一、模式核心:模板方法定义算法骨架,具体步骤延迟到子类实现

在软件开发中,经常会遇到这样的情况:某个算法的步骤是固定的,但具体步骤的实现可能因不同情况而有所不同。例如,在电商系统中,订单的处理流程通常包括创建订单、支付、发货、通知用户等步骤,但不同类型的订单(如普通订单、秒杀订单)在支付和发货环节的实现可能不同。

模板方法模式(Template Method Pattern) 定义了一个算法的骨架,将算法中的具体步骤延迟到子类中实现。模板方法模式让子类在不改变算法结构的前提下,重新定义算法中的某些具体步骤,核心解决:

  • 代码复用:将算法的公共步骤封装在父类中,避免子类重复实现。
  • 算法扩展:子类可以通过重写父类的具体步骤来扩展算法的实现。
  • 流程控制:父类控制算法的整体流程,子类负责具体步骤的实现,确保算法的步骤顺序不变。

核心思想与 UML 类图(PlantUML 语法)

模板方法模式包含抽象类(Abstract Class)和具体子类(Concrete Class)。抽象类中定义了模板方法(Template Method)和若干基本方法(Primitive Methods),模板方法定义了算法的骨架,基本方法包括具体方法和抽象方法,具体方法在抽象类中已经实现,抽象方法在子类中实现。

二、核心实现:电商订单处理流程

1. 定义抽象订单类(模板类)

java 复制代码
public abstract class AbstractOrder {
    // 模板方法:订单处理流程
    public final void processOrder() {
        createOrder(); // 创建订单(具体方法,在抽象类中实现)
        pay(); // 支付(抽象方法,由子类实现)
        deliverGoods(); // 发货(抽象方法,由子类实现)
        notifyUser(); // 通知用户(具体方法,在抽象类中实现)
    }

    // 具体方法:创建订单(公共步骤,无需子类重写)
    protected void createOrder() {
        System.out.println("创建订单");
    }

    // 抽象方法:支付(不同订单类型实现不同)
    protected abstract void pay();

    // 抽象方法:发货(不同订单类型实现不同)
    protected abstract void deliverGoods();

    // 具体方法:通知用户(公共步骤,无需子类重写)
    protected void notifyUser() {
        System.out.println("通知用户订单处理完成");
    }
}

2. 实现具体订单类(普通订单)

java 复制代码
public class NormalOrder extends AbstractOrder {
    @Override
    protected void pay() {
        System.out.println("普通订单使用支付宝支付");
    }

    @Override
    protected void deliverGoods() {
        System.out.println("普通订单使用普通快递发货");
    }
}

3. 实现具体订单类(秒杀订单)

java 复制代码
public class FlashSaleOrder extends AbstractOrder {
    @Override
    protected void pay() {
        System.out.println("秒杀订单使用微信支付(优先扣款)");
    }

    @Override
    protected void deliverGoods() {
        System.out.println("秒杀订单使用顺丰快递加急发货");
    }
}

4. 客户端使用模板方法模式

java 复制代码
public class ClientDemo {
    public static void main(String[] args) {
        // 处理普通订单
        AbstractOrder normalOrder = new NormalOrder();
        System.out.println("处理普通订单:");
        normalOrder.processOrder();

        System.out.println("\n处理秒杀订单:");
        AbstractOrder flashSaleOrder = new FlashSaleOrder();
        flashSaleOrder.processOrder();
    }
}

输出结果

plaintext 复制代码
处理普通订单:
创建订单
普通订单使用支付宝支付
普通订单使用普通快递发货
通知用户订单处理完成

处理秒杀订单:
创建订单
秒杀订单使用微信支付(优先扣款)
秒杀订单使用顺丰快递加急发货
通知用户订单处理完成

三、进阶:钩子方法(Hook Method)增强模板灵活性

在模板方法模式中,可以通过 钩子方法 来增加算法的灵活性。钩子方法是一个在抽象类中默认实现的方法,子类可以根据需要重写该方法,以控制算法的流程。

1. 添加钩子方法(是否需要短信通知)

java 复制代码
public abstract class AbstractOrder {
    // ... 其他方法不变 ...

    // 钩子方法:是否需要通知用户(默认需要)
    protected boolean needNotifyUser() {
        return true;
    }

    // 模板方法中调用钩子方法
    public final void processOrder() {
        createOrder();
        pay();
        deliverGoods();
        if (needNotifyUser()) { // 根据钩子方法结果决定是否通知用户
            notifyUser();
        }
    }
}

2. 子类重写钩子方法(秒杀订单不需要通知用户)

java 复制代码
public class FlashSaleOrder extends AbstractOrder {
    // ... 其他方法不变 ...

    @Override
    protected boolean needNotifyUser() {
        return false; // 秒杀订单不通知用户
    }
}

3. 客户端测试钩子方法效果

java 复制代码
public class ClientDemo {
    public static void main(String[] args) {
        // ... 处理普通订单 ...

        System.out.println("\n处理秒杀订单(不通知用户):");
        AbstractOrder flashSaleOrder = new FlashSaleOrder();
        flashSaleOrder.processOrder();
    }
}

输出结果

plaintext 复制代码
处理秒杀订单(不通知用户):
创建订单
秒杀订单使用微信支付(优先扣款)
秒杀订单使用顺丰快递加急发货

四、框架与源码中的模板方法实践

1. Java 的 AbstractList 类

Java 集合框架中的 AbstractList 类是模板方法模式的典型应用。AbstractList 定义了列表的基本操作流程,如 addget 等方法,具体的实现由子类(如 ArrayListLinkedList)完成。

java 复制代码
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    // 模板方法:获取元素
    public E get(int index) {
        throw new AbstractMethodError(); // 抽象方法,由子类实现
    }

    // 具体方法:添加元素(基于 get 和 set 实现)
    public boolean add(E e) {
        add(size(), e); // 调用子类实现的 add(int, E) 方法
        return true;
    }
}

2. Spring 的 JdbcTemplate

Spring 框架中的 JdbcTemplate 使用模板方法模式封装了 JDBC 的操作流程。JdbcTemplate 定义了执行 SQL 的模板方法(如 queryForObject),具体的结果映射由回调接口(如 RowMapper)实现。

java 复制代码
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
    // 模板方法:查询单个对象
    public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args) {
        return execute(sql, new PreparedStatementCallback<T>() {
            @Override
            public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
                ps.execute();
                ResultSet rs = ps.getResultSet();
                return rowMapper.mapRow(rs, 1); // 回调接口实现结果映射
            }
        }, args);
    }
}

五、避坑指南:正确使用模板方法模式的 3 个要点

1. 合理设计模板方法的访问权限

模板方法通常定义为 final 方法,防止子类重写,确保算法骨架的稳定性。如果需要子类重写模板方法,可以将其定义为 protected 方法,但需谨慎使用,避免破坏算法结构。

2. 控制抽象类中的抽象方法数量

抽象类中的抽象方法应尽可能少,只包含那些必须由子类实现的步骤。如果抽象方法过多,会导致子类的实现复杂度增加,违背模板方法模式的初衷。

3. 避免在模板方法中调用子类的方法

在模板方法中应优先调用抽象类中的方法,避免直接调用子类的方法,否则可能导致循环依赖或子类未初始化的问题。如果需要调用子类的方法,可以通过抽象方法或钩子方法实现。

六、总结:何时该用模板方法模式?

适用场景 核心特征 典型案例
算法步骤固定 算法的步骤顺序是固定的,但具体步骤实现可变 订单处理流程、考试流程
代码复用 多个子类有共同的算法骨架和部分公共代码 日志记录器、文件处理器
流程控制 需要确保算法步骤的执行顺序不被篡改 工作流引擎、游戏关卡流程

模板方法模式通过将算法骨架与具体实现分离,实现了代码的复用和算法的扩展,是一种非常实用的设计模式。下一篇我们将深入探讨迭代器模式,解析如何统一遍历不同数据结构的方式,敬请期待!

扩展思考:模板方法模式 vs 策略模式

类型 核心思想 适用场景
模板方法模式 定义算法骨架,具体步骤由子类实现 算法步骤固定,部分步骤需变化
策略模式 定义一系列算法,可动态切换算法实现 算法可动态选择,客户端主动切换

理解这种差异,能帮助我们在不同场景下选择更合适的设计模式。

相关推荐
一只叫煤球的猫6 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
bobz9657 小时前
tcp/ip 中的多路复用
后端
bobz9657 小时前
tls ingress 简单记录
后端
皮皮林5518 小时前
IDEA 源码阅读利器,你居然还不会?
java·intellij idea
你的人类朋友8 小时前
什么是OpenSSL
后端·安全·程序员
bobz9658 小时前
mcp 直接操作浏览器
后端
前端小张同学11 小时前
服务器部署 gitlab 占用空间太大怎么办,优化思路。
后端
databook11 小时前
Manim实现闪光轨迹特效
后端·python·动效
武子康12 小时前
大数据-98 Spark 从 DStream 到 Structured Streaming:Spark 实时计算的演进
大数据·后端·spark
该用户已不存在12 小时前
6个值得收藏的.NET ORM 框架
前端·后端·.net