教程中心 / Java教程 / 模板方法

模板方法

10分钟 设计模式

什么是模板方法模式?

模板方法模式(Template Method Pattern)是一种行为型设计模式,它在一个方法中定义一个算法的骨架,将某些具体步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些特定步骤。

生活比喻

想象你是一位美食节目主持人,正在教观众做奶茶。步骤是固定的:
1️⃣ 烧水
2️⃣ 加入茶叶
3️⃣ 加入牛奶
4️⃣ 加入糖
5️⃣ 倒入杯中

无论做什么口味(珍珠奶茶、椰果奶茶),步骤1、2、5都是一样的,只是步骤3、4不同。模板方法就是这样——定义算法的骨架,让子类决定某些步骤的具体实现。

模板方法模式结构:

┌─────────────────────────────────────────────────────────────┐
│                   AbstractClass(抽象类)                    │
│  ┌─────────────────────────────────────────────────────┐  │
│  │  final templateMethod() {  ← 模板方法,用final防止重写│  │
│  │      step1();                                        │  │
│  │      step2();                                        │  │
│  │      hook();     ← 钩子方法,有默认实现,子类可选重写  │  │
│  │  }                                                   │  │
│  │                                                      │  │
│  │  abstract step1();  ← 抽象方法,必须由子类实现       │  │
│  │  abstract step2();                                   │  │
│  └─────────────────────────────────────────────────────┘  │
└───────────────────────────┬─────────────────────────────────┘
                            │ 继承
        ┌───────────────────┼───────────────────┐
        ▼                   ▼                   ▼
┌───────────┐       ┌───────────┐       ┌───────────┐
│ConcreteA  │       │ConcreteB  │       │ConcreteC  │
│实现step1  │       │实现step1  │       │实现step1  │
│实现step2  │       │实现step2  │       │实现step2  │
└───────────┘       └───────────┘       └───────────┘

完整实现

1. 创建抽象基类

// 抽象基类 - 定义模板方法和抽象步骤
public abstract class DataProcessor {

    // 模板方法 - final防止被子类重写
    public final void process() {
        System.out.println("===== 开始处理 =====");
        readData();
        processData();
        saveData();
        System.out.println("===== 处理完成 =====\n");
    }

    // 抽象方法 - 必须由子类实现
    protected abstract void readData();
    protected abstract void processData();

    // 具体方法 - 有默认实现
    protected void saveData() {
        System.out.println("保存到默认位置...");
    }
}

2. 实现具体子类

// 处理CSV数据
class CSVProcessor extends DataProcessor {
    private String csvData = "id,name,age\n1,张三,25\n2,李四,30";

    @Override
    protected void readData() {
        System.out.println("[CSV] 从文件读取CSV数据...");
        System.out.println("原始数据:\n" + csvData);
    }

    @Override
    protected void processData() {
        System.out.println("[CSV] 解析CSV行...");
        String[] lines = csvData.split("\n");
        for (int i = 1; i < lines.length; i++) {
            String[] fields = lines[i].split(",");
            System.out.printf("  记录%d: id=%s, name=%s, age=%s%n",
                i, fields[0], fields[1], fields[2]);
        }
    }

    @Override
    protected void saveData() {
        System.out.println("[CSV] 保存为users.csv文件...");
    }
}

// 处理JSON数据
class JSONProcessor extends DataProcessor {
    private String jsonData = "[{\"id\":1,\"name\":\"王五\",\"age\":28},{\"id\":2,\"name\":\"赵六\",\"age\":35}]";

    @Override
    protected void readData() {
        System.out.println("[JSON] 从API获取JSON数据...");
        System.out.println("原始数据:\n" + jsonData);
    }

    @Override
    protected void processData() {
        System.out.println("[JSON] 解析JSON对象...");
        System.out.println("  解析完成,共2条记录");
    }
}

3. 使用

public class TemplateMethodDemo {
    public static void main(String[] args) {
        System.out.println("===== 处理CSV =====");
        DataProcessor csvProcessor = new CSVProcessor();
        csvProcessor.process();

        System.out.println("===== 处理JSON =====");
        DataProcessor jsonProcessor = new JSONProcessor();
        jsonProcessor.process();
    }
}

钩子方法详解

钩子方法(Hook Method)是模板方法中的一个可选方法,它有默认实现,子类可以选择性地覆盖它。钩子方法用于提供"钩子"让子类在特定时机介入。

// 饮料抽象类 - 包含钩子方法
abstract class Beverage {

    // 模板方法
    public final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        if (wantCondiments()) {  // 钩子方法!
            addCondiments();
        }
    }

    // 具体方法 - 通用实现
    protected void boilWater() {
        System.out.println("烧水");
    }

    protected void pourInCup() {
        System.out.println("倒入杯中");
    }

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

    // 钩子方法 - 有默认实现,子类可选重写
    protected boolean wantCondiments() {
        return true;  // 默认要加配料
    }
}

// 咖啡
class Coffee extends Beverage {
    @Override
    protected void brew() {
        System.out.println("冲泡咖啡");
    }

    @Override
    protected void addCondiments() {
        System.out.println("加入糖和牛奶");
    }

    // 覆盖钩子方法 - 咖啡不加糖
    @Override
    protected boolean wantCondiments() {
        return false;
    }
}

// 茶
class Tea extends Beverage {
    @Override
    protected void brew() {
        System.out.println("浸泡茶叶");
    }

    @Override
    protected void addCondiments() {
        System.out.println("加入柠檬");
    }
}

// 可可
class Cocoa extends Beverage {
    @Override
    protected void brew() {
        System.out.println("冲泡可可粉");
    }

    @Override
    protected void addCondiments() {
        System.out.println("加入棉花糖");
    }
}

public class HookMethodDemo {
    public static void main(String[] args) {
        System.out.println("===== 咖啡(不加配料)=====");
        Beverage coffee = new Coffee();
        coffee.prepareRecipe();

        System.out.println("\n===== 茶(加配料)=====");
        Beverage tea = new Tea();
        tea.prepareRecipe();
    }
}

模板方法 vs 策略模式

对比项模板方法策略模式
目的定义算法骨架,复用不变部分封装可互换的算法
实现方式继承,子类实现抽象方法组合,通过接口注入
侧重点复用和扩展切换和替换
灵活性低(继承结构固定)高(可以运行时切换)
典型场景算法骨架、数据处理流程支付方式、排序算法

应用场景

1

数据处理流程

读取 → 验证 → 转换 → 保存(固定流程,具体步骤不同)

2

框架扩展点

Spring的AbstractBeanDefinitionReader等

3

测试框架

JUnit的TestCase类

注意事项

模板方法必须用final修饰!

模板方法是算法的骨架,如果被重写,整个算法的结构可能被破坏。

小结

  • 模板方法模式定义算法骨架,将某些步骤延迟到子类
  • 抽象方法必须由子类实现
  • 具体方法有默认实现,可被子类继承
  • 钩子方法是有默认实现的可选方法
  • final templateMethod()防止算法骨架被破坏
  • 模板方法复用不变部分,策略模式切换算法
☕ Java 在线代码编辑器
📝 运行结果