0%

模板方法模式

模板方法模式(Template Method Pattern):固定算法骨架,灵活定制步骤

模板方法模式是行为型设计模式的一种,核心思想是在抽象类中定义一个算法的骨架(模板方法),将算法中可变的步骤延迟到子类中实现,使得子类在不改变算法整体结构的前提下,可自定义某些特定步骤。这种模式就像 “文档模板”—— 模板规定了文档的整体结构(如标题、正文、结尾),用户只需填充具体内容即可,核心是 “固定骨架,灵活填充”。

模板方法模式的核心结构

模板方法模式

模板方法模式通过抽象类和子类的配合实现算法的复用与扩展,包含三种关键方法:

模板方法(Template Method)

  • 定义在抽象类中,描述算法的骨架,按顺序调用其他方法(抽象方法、钩子方法),构成完整的算法流程。
  • 通常用final修饰,防止子类修改算法结构。
  • 示例:makeBeverage()(制作饮料的模板方法,依次调用 “煮水→冲泡→倒杯→加配料”)。

抽象方法(Abstract Method)

  • 由抽象类声明、子类必须实现的方法,对应算法中可变的步骤。
  • 示例:brew()(冲泡,咖啡和茶的冲泡方式不同)、addCondiments()(加配料,咖啡加奶,茶加糖)。

钩子方法(Hook Method)

  • 由抽象类声明并提供默认实现(可为空),子类可选择重写,用于控制算法流程或添加额外逻辑。
  • 示例:isAddCondiments()(是否加配料,子类可通过返回false跳过该步骤)。

代码实现示例

以 “饮料制作流程” 为例,展示模板方法模式的实现:咖啡和茶的制作流程基本相同(煮水→冲泡→倒杯→加配料),但冲泡方式和配料不同,通过模板方法固定流程,子类实现差异步骤。

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
28
29
30
31
32
33
34
// 抽象类:饮料制作模板
public abstract class Beverage {

// 模板方法:定义算法骨架(不可被子类修改)
public final void makeBeverage() {
boilWater(); // 步骤1:煮水(固定)
brew(); // 步骤2:冲泡(可变,抽象方法)
pourInCup(); // 步骤3:倒杯(固定)
if (isAddCondiments()) { // 钩子方法:控制是否执行步骤4
addCondiments(); // 步骤4:加配料(可变,抽象方法)
}
}

// 抽象方法1:冲泡(子类必须实现)
protected abstract void brew();

// 抽象方法2:加配料(子类必须实现)
protected abstract void addCondiments();

// 固定方法1:煮水(所有饮料通用)
private void boilWater() {
System.out.println("煮水至100℃");
}

// 固定方法2:倒杯(所有饮料通用)
private void pourInCup() {
System.out.println("倒入杯中");
}

// 钩子方法:默认加配料,子类可重写(如无配料则返回false)
protected boolean isAddCondiments() {
return true; // 默认执行加配料步骤
}
}

2. 具体子类(实现抽象方法,可选重写钩子方法)

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
// 具体子类1:咖啡
public class Coffee extends Beverage {

@Override
protected void brew() {
System.out.println("用沸水冲泡咖啡粉"); // 咖啡的冲泡方式
}

@Override
protected void addCondiments() {
System.out.println("添加牛奶和糖"); // 咖啡的配料
}

// 可选:重写钩子方法(例如黑咖啡不加配料)
// @Override
// protected boolean isAddCondiments() {
// return false;
// }
}

// 具体子类2:茶
public class Tea extends Beverage {

@Override
protected void brew() {
System.out.println("用80℃热水浸泡茶叶"); // 茶的冲泡方式
}

@Override
protected void addCondiments() {
System.out.println("添加蜂蜜"); // 茶的配料
}
}

3. 客户端使用

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 class TemplateMethodDemo {
public static void main(String[] args) {
// 制作咖啡
Beverage coffee = new Coffee();
System.out.println("制作咖啡:");
coffee.makeBeverage();
// 输出:
// 制作咖啡:
// 煮水至100℃
// 用沸水冲泡咖啡粉
// 倒入杯中
// 添加牛奶和糖

// 制作茶
Beverage tea = new Tea();
System.out.println("\n制作茶:");
tea.makeBeverage();
// 输出:
// 制作茶:
// 煮水至100℃
// 用80℃热水浸泡茶叶
// 倒入杯中
// 添加蜂蜜
}
}

模板方法模式的核心优势

  1. 固定算法结构,复用公共逻辑
    抽象类中的模板方法固定了算法的整体流程,公共步骤(如煮水、倒杯)在抽象类中实现,避免子类重复编码,提高代码复用性。
  2. 灵活扩展可变步骤
    子类通过实现抽象方法自定义可变步骤(如咖啡和茶的不同冲泡方式),符合开闭原则(扩展时无需修改模板方法)。
  3. 控制子类扩展
    模板方法用final修饰,确保子类无法修改算法结构;钩子方法允许子类在特定节点扩展,既灵活又可控。
  4. 封装不变,扩展可变
    清晰区分算法中的不变部分(模板方法和固定步骤)与可变部分(抽象方法和钩子方法),逻辑层次分明。

适用场景

  1. 算法流程固定,步骤部分可变
    当多个类的算法流程基本一致,仅部分步骤存在差异时(如:
    • 流程类业务:登录流程(输入账号→验证→授权→跳转,验证方式可变)。
    • 框架回调:如 Spring 的InitializingBeanafterPropertiesSet()方法在初始化后固定调用)。
    • 测试用例:单元测试的setUp()test()tearDown()流程,test()方法可变。
  2. 需要控制子类扩展
    当希望子类仅能在特定步骤扩展,而不改变整体流程时(如线程池ThreadPoolExecutorbeforeExecute()afterExecute()钩子方法)。
  3. 重构重复代码
    当多个子类存在重复的流程代码时,可将流程提取为模板方法,差异步骤抽象为抽象方法,减少冗余。

优缺点分析

优点

  • 代码复用率高:公共流程在抽象类中实现,避免重复编码。
  • 扩展性好:新增功能只需添加子类,实现抽象方法即可。
  • 算法结构稳定:模板方法固定流程,确保子类遵循统一的算法逻辑。

缺点

  • 灵活性受限:子类必须遵循模板方法定义的流程,无法改变步骤顺序。
  • 抽象类与子类耦合:子类的实现依赖抽象类的模板方法,若模板方法修改,可能影响所有子类。
  • 学习成本:新手可能难以理解 “骨架与步骤” 的分离设计。

经典应用案例

  1. Java 集合框架的AbstractList
    AbstractList定义了get()size()等抽象方法,模板方法indexOf()lastIndexOf()基于这些抽象方法实现,子类(如ArrayListLinkedList)只需实现核心抽象方法即可复用模板方法。
  2. Spring 的AbstractApplicationContext
    Spring 容器初始化流程(refresh()方法)是典型的模板方法,包含 “准备刷新→获取 bean 工厂→加载 bean 定义→初始化 bean” 等步骤,部分步骤通过钩子方法(如postProcessBeanFactory())允许子类扩展。
  3. JUnit 的测试用例
    JUnit4 的TestCase类中,runBare()方法作为模板方法,按setUp()runTest()tearDown()顺序执行,runTest()为抽象方法,由用户编写具体测试逻辑。
  4. 线程池ThreadPoolExecutor
    ThreadPoolExecutorexecute()方法定义了任务执行的流程,beforeExecute()afterExecute()等钩子方法允许子类在任务执行前后添加自定义逻辑(如日志记录、监控)。

总结

模板方法模式通过 “抽象类定义骨架,子类实现步骤” 的方式,完美平衡了算法的稳定性与灵活性。其核心价值在于复用公共流程,同时允许子类定制差异步骤,特别适合流程固定但细节可变的场景。在框架设计中,模板方法模式被广泛应用,如 Spring、JUnit 等,通过定义模板方法为用户提供扩展点,同时确保框架的核心流程稳定

欢迎关注我的其它发布渠道