1. 程序员的造物主体验

假设您正准备制作一杯咖啡:研磨咖啡豆→加热水→注水冲泡→添加牛奶。换作制作茶时:准备茶叶→烧开水→浸泡茶叶→加柠檬。这两种饮品的制作过程都存在确定的步骤序列,只是某些环节存在差异——这正是模板方法模式要解决的核心问题。

我们的代码世界每天都在重复类似的场景:流程大体相同,但某些步骤需要灵活调整。这时候Java模板方法模式就化身程序世界的脚手架,帮助我们构建既稳定又可扩展的架构。

2. 模式定义与技术解析

模板方法模式属于行为型设计模式,其结构组成包含:

// 技术栈:Java 17
public abstract class BeverageMaker {
    
    // 模板方法(不可被重写)
    public final void prepareBeverage() {
        grindMaterial();
        boilWater();
        brew();
        addCondiments();
    }

    // 抽象方法(子类必须实现)
    protected abstract void grindMaterial();
    protected abstract void brew();

    // 公共方法(所有子类复用)
    protected void boilWater() {
        System.out.println("将水烧至95℃");
    }

    // 钩子方法(子类可选覆盖)
    protected void addCondiments() {
        // 空实现作为默认行为
    }
}

示例解析:

  • prepareBeverage() 是封装流程的模板方法(final修饰确保不可篡改)
  • grindMaterial()brew() 必须由子类实现(类似咖啡豆研磨与茶叶处理)
  • boilWater() 是公共步骤的复用实现
  • addCondiments() 作为钩子方法(Hook Method)提供扩展点

3. 模式实现全景演示

示例一:咖啡制备系统

class CoffeeMaker extends BeverageMaker {
    @Override
    protected void grindMaterial() {
        System.out.println("将阿拉比卡咖啡豆研磨成中粗颗粒");
    }

    @Override
    protected void brew() {
        System.out.println("使用V60滤杯进行三段式手冲");
    }

    @Override
    protected void addCondiments() {
        System.out.println("添加15ml香草糖浆和打发的奶泡");
    }
}

class TeaMaker extends BeverageMaker {
    @Override
    protected void grindMaterial() {
        System.out.println("碾碎锡兰红茶茶叶至碎末状");
    }

    @Override
    protected void brew() {
        System.out.println("使用茶漏进行90秒焖泡");
    }

    // 不覆盖钩子方法(清茶不添加配料)
}

示例二:电商支付流程

public abstract class PaymentProcessor {
    
    public final void executePayment() {
        validateOrder();
        calculateFee();
        performPayment();
        if (needNotification()) {
            sendReceipt();
        }
        updateInventory();
    }

    protected abstract void performPayment();

    protected void validateOrder() {
        System.out.println("验证订单有效性:库存检查→风控校验");
    }

    protected void calculateFee() {
        System.out.println("计算手续费:基础费率×支付渠道系数");
    }

    protected boolean needNotification() {
        return true; // 默认开启通知
    }

    protected void sendReceipt() {
        System.out.println("发送支付凭证到用户邮箱和手机");
    }

    protected void updateInventory() {
        System.out.println("扣除预占库存并更新销量数据");
    }
}

class AlipayProcessor extends PaymentProcessor {
    @Override
    protected void performPayment() {
        System.out.println("调用支付宝APP支付接口,处理加密请求");
    }

    @Override
    protected boolean needNotification() {
        return false; // 支付宝已有推送功能
    }
}

4. 技术耦合深度解析

应用场景矩阵

场景特征 适用案例
存在确定流程规范 金融交易流程、医疗问诊流程
需要差异化步骤实现 各支付渠道对接、多端数据同步
扩展点需要精确控制 权限校验流程、审计日志记录
公共行为需要集中维护 缓存更新策略、消息重试机制

技术优势详解

  1. 流程管控力:杜绝子类破坏核心流程(如模板方法final修饰)
  2. 代码复用性:公共方法集中维护,减少重复代码
  3. 扩展灵活性:通过抽象方法和钩子方法提供双重扩展机制
  4. 复杂度隔离:高层模块专注流程,底层模块专注具体实现

潜在缺陷警示

  1. 过度分层陷阱:简单场景使用该模式会增加不必要的复杂度
  2. 类爆炸风险:每个流程变体都需要创建具体子类
  3. 逆向流程限制:难以处理需要反向调整执行顺序的场景
  4. 继承强耦合:父类修改可能引发所有子类的连锁反应

5. 最佳实践注意事项

设计原则指导

  • 好莱坞原则:父类主动调用子类方法,但禁止子类调用父类
  • 单一职责深化:每个具体类只负责自身步骤的实现
  • 开闭原则实践:通过增加子类扩展,而不是修改现有类

典型误用场景

// 反模式示例:违反模板方法不可重写的原则
public class WrongProcessor extends PaymentProcessor {
    @Override
    public void executePayment() {  // 错误地覆盖final方法
        // ...自定义流程...
    }
}

关联技术对比

策略模式VS模板模式

  • 策略模式:通过接口切换整个算法(黑盒)
  • 模板模式:通过继承调整算法局部(白盒)

工厂方法模式与模板模式

  • 工厂方法关注对象创建流程
  • 模板方法关注行为执行流程

6. 架构演进思考

在现代微服务架构中,模板方法模式的应用正在发生进化。以Spring框架中的JdbcTemplate为例:

public class UserDao extends JdbcDaoSupport {
    
    public List<User> findAll() {
        return getJdbcTemplate().query(
            "SELECT * FROM users", 
            (rs, rowNum) -> new User(
                rs.getLong("id"),
                rs.getString("name")
            )
        );
    }
}

// 模板方法隐藏在JdbcTemplate中:
public <T> List<T> query(String sql, RowMapper<T> rowMapper) {
    // 1. 获取连接
    // 2. 创建声明
    // 3. 执行查询
    // 4. 处理结果集 ← 这个步骤通过RowMapper回调
    // 5. 释放资源
}

这种实现将经典模式的继承关系转换为基于回调的组合模式,更适应现代软件开发对低耦合的需求。

7. 模式进化与展望

随着函数式编程的普及,Java 8引入的Lambda表达式为模板方法模式提供了新的实现思路:

public class PaymentTemplate {
    private final Runnable validate;
    private final Runnable calculate;
    private final Runnable perform;

    public PaymentTemplate(Runnable validate, Runnable calculate, Runnable perform) {
        this.validate = validate;
        this.calculate = calculate;
        this.perform = perform;
    }

    public void process() {
        validate.run();
        calculate.run();
        perform.run();
        System.out.println("执行通用结算后处理");
    }
}

// 使用方式:
new PaymentTemplate(
    () -> System.out.println("自定义校验逻辑"),
    () -> System.out.println("特殊费率计算"),
    () -> System.out.println("微信支付处理")
).process();

这种演进使得模板方法突破了传统继承体系的限制,在保持流程控制的同时,获得了更高的灵活性。