什么是模板方法模式?
模板方法模式(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()防止算法骨架被破坏
- 模板方法复用不变部分,策略模式切换算法