基本概念

模板方法模式:属于行为型设计模式,定义一个算法的骨架(模板),将算法中的某些步骤延迟到子类中实现,从而让子类在不改变算法整体结构的前提下,重新定义算法中的特定步骤。

核心结构

  1. 抽象类(Abstract Class):定义模板方法和基本方法(抽象或具体),模板方法定义算法骨架。
  2. 具体子类(Concrete Class):实现抽象类中的抽象方法,完成算法的具体步骤。

示例说明

场景:设计一个饮品制作系统,制作咖啡和茶的流程相似(煮水、泡制、倒入杯子),但泡制方式和调料添加不同。

  1. 饮品抽象类, 设计定义通用的方法和流程
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
// 抽象饮品类(模板方法模式的核心)
public abstract class Beverage {
// 模板方法:定义饮品制作的整体流程,用final防止子类覆盖
public final void prepareRecipe() {
boilWater(); // 煮沸水(具体方法,所有饮品相同)
brew(); // 泡制(抽象方法,由子类实现)
pourIntoCup(); // 倒入杯子(具体方法,所有饮品相同)
addCondiments(); // 添加调料(抽象方法,由子类实现)
}

protected void boilWater() {
System.out.println("煮沸水");
}

protected void pourIntoCup() {
System.out.println("倒入杯子");
}

// 由子类实现
abstract void brew();

// 由子类实现
abstract void addCondiments();
}

  1. 子类实现:咖啡、茶
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

// 具体子类:咖啡
public class Coffee extends Beverage {
@Override
void brew() {
System.out.println("用咖啡粉冲泡");
}

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

// 具体子类:茶
public class Tea extends Beverage {
@Override
void brew() {
System.out.println("用茶叶浸泡");
}

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

  1. 验证测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14

// 用户调用
public class Test {
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();
}
}

  1. 结果展示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14

输出结果:
制作咖啡:
煮沸水
用咖啡粉冲泡
倒入杯子
添加糖和牛奶

制作茶:
煮沸水
用茶叶浸泡
倒入杯子
添加柠檬

类图图下:

模板方法

  • 模板方法(prepareRecipe):定义了饮品制作的固定流程(煮水→泡制→倒杯→加调料),确保算法骨架不变。
  • 差异化步骤(brew、addCondiments):由子类实现,不同饮品的泡制和调料添加方式不同。
  • 代码复用:抽象类中封装了通用步骤(boilWater、pourIntoCup),避免重复代码。

如有必要,抽象类的方法可以采用钩子的形式由子类决定是否使用,如在执行某个步骤时,可以由子类决定是否覆盖重写。这就涉及到一个设计原则:
好莱坞原则:不要打电话给(调用)我们,我们会打电话给(调用)你

好莱坞原则给我们一种防止“依赖腐烂”的方法,“依赖腐烂”的例子如高层组件依赖于低层组件,低层组件又依赖于高层组件,而高层组件又依赖于横向组件,横向组件又依赖于低层组件等。当腐烂蔓延时,没有人可以轻易理解系统是如何设计等。有了好莱坞原则,将允许低层组件把自己挂进高层组件,由高层组件决定是否使用,如何使用。

用到好莱坞原则的模式: 工厂方法模式、观察者模式

总结

优点:

  • 复用性高:抽象类封装通用逻辑,子类只需实现差异化逻辑。
  • 扩展性好:新增子类时无需修改模板方法,符合开闭原则
  • 统一执行顺序:模板方法确保算法步骤的执行顺序,避免逻辑混乱。

缺点:

  • 子类依赖抽象类:子类需严格遵循抽象类定义的模板,灵活性受限于算法骨架。
  • 抽象类可能复杂:若模板方法包含过多步骤,抽象类可能变得臃肿。

使用场景:

  • 流程固定但细节不同的场景:如文件读取(打开→读取→关闭)、HTTP请求处理(连接→请求→响应→断开)。
  • 框架设计:如JDK中的HttpServletdoGetdoPost是模板方法的具体步骤)、Spring框架的模板类(如JdbcTemplate)。
  • Comparable<T> 接口和Arrays.sort(): 实现比较大小接口,调用sort()实现排序

与策略模式的区别:模板方法通过继承实现算法变化,策略模式通过组合实现算法变化。模板方法适合“算法骨架固定、部分步骤变化”,策略模式适合“整个算法可替换”。