模板模式(Template Pattern)是一种行为型设计模式,它定义了一个操作的骨架流程,将某些步骤延迟到子类中实现。核心思想是固定算法结构,灵活定制具体步骤,使得子类可以在不改变算法整体结构的情况下,重写某些特定步骤。

简单来说,模板模式就像 “食谱”—— 食谱规定了做菜的步骤(如准备食材→翻炒→调味→出锅),但具体用什么食材、放多少调料可以由使用者(子类)自行决定。

模版模式的UML表示如下:

image.png

在上图中:

  • 抽象模板类(AbstractClass)
    • 定义templateMethod():作为算法骨架,按固定顺序调用各个步骤方法。
    • 声明抽象方法(primitiveOperation1()primitiveOperation2()):这些步骤由子类实现,是算法中可变的部分。
    • 提供具体方法(concreteOperation()):算法中固定不变的步骤,父类直接实现。
    • 可选钩子方法(hook()):默认空实现或基础实现,子类可根据需要重写,用于控制算法流程。
  • 具体子类(ConcreteClassA/B):实现父类的抽象方法,定制算法中的可变步骤,也可选择重写钩子方法。
  • 客户端:通过抽象模板类调用templateMethod(),无需关心具体子类,实现对不同算法变体的统一调用。

模板模式的核心价值是封装不变部分,扩展可变部分,常用于框架设计(如 Spring 的JdbcTemplate、Servlet 的doGet/doPost),让框架控制流程,用户只需填充具体业务逻辑。

下面以 “饮品制作流程” 为例,展示模板模式的具体实现。制作咖啡和茶的流程有相似的骨架(煮水→冲泡→倒入杯子→添加调料),但具体步骤的实现不同,适合用模板模式统一流程并差异化实现细节。

首先,我们定义一个抽象的模版类,它定义了饮品制作的流程骨架:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 抽象模板类:定义饮品制作的骨架流程
abstract class BeverageMaker {
/**
* 模板方法:固定算法步骤,用final修饰防止子类修改流程
*/
public final void makeBeverage() {
boilWater(); // 步骤1:煮水(固定步骤)
brew(); // 步骤2:冲泡(子类实现)
pourInCup(); // 步骤3:倒入杯子(固定步骤)
if (needCondiments()) { // 钩子方法:判断是否需要添加调料
addCondiments(); // 步骤4:添加调料(子类实现)
}
}

// 抽象方法:冲泡(咖啡和茶的实现不同)
protected abstract void brew();

// 抽象方法:添加调料(咖啡和茶的调料不同)
protected abstract void addCondiments();

// 具体方法:煮水(所有饮品都需要,固定实现)
private void boilWater() {
System.out.println("煮水至100℃");
}

// 具体方法:倒入杯子(所有饮品都需要,固定实现)
private void pourInCup() {
System.out.println("将饮品倒入杯子");
}

/**
* 钩子方法:默认需要添加调料,子类可重写改变行为
* 用于控制算法流程的可选步骤
*/
protected boolean needCondiments() {
return true; // 默认需要添加调料
}
}

制作饮品的抽象步骤是相同的,但是具体执行是有差异的。比如泡茶和泡咖啡的每一个步骤要做的事情是不一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 具体子类:咖啡制作
class CoffeeMaker extends BeverageMaker {
@Override
protected void brew() {
System.out.println("用沸水冲泡咖啡粉");
}

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

// 可选:重写钩子方法(例如某些咖啡不加调料)
// @Override
// protected boolean needCondiments() {
// return false;
// }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 具体子类:茶制作
class TeaMaker extends BeverageMaker {
@Override
protected void brew() {
System.out.println("用沸水浸泡茶叶");
}

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

// 重写钩子方法:茶可以选择不加调料
@Override
protected boolean needCondiments() {
// 实际场景中可以根据用户输入决定,这里简化为固定返回false
return false;
}
}

客户端调用代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 客户端:使用模板模式制作饮品
public class BeverageShop {
public static void main(String[] args) {
// 制作咖啡
System.out.println("=== 制作咖啡 ===");
BeverageMaker coffee = new CoffeeMaker();
coffee.makeBeverage();

// 制作茶
System.out.println("\n=== 制作茶 ===");
BeverageMaker tea = new TeaMaker();
tea.makeBeverage();
}
}

使用模板方法的优势在于:

  • 代码复用:父类封装了公共步骤(如煮水、倒杯子),避免子类重复实现。
  • 流程控制:父类固定算法结构,确保所有子类遵循统一的流程规范(如必须先煮水再冲泡)。
  • 灵活扩展:子类通过重写抽象方法和钩子方法,可在不改变流程的前提下定制具体行为。