AI速览:模板方法模式通过抽象父类(如AbstractDisplay)定义固定算法骨架(如display()方法),将可变步骤(如open()print())延迟到子类实现,实现代码复用与灵活扩展。典型应用包括JUnit测试生命周期、Servlet请求处理等场景,核心是“流程框架由父类定,具体步骤子类改”,优点在于避免重复代码,但可能导致父类臃肿且继承强耦合。本质是“标准化流水线,细节自由装配”。

设计模式笔记:Template Method模式(模板方法模式)

什么是模板方法模式?

想象你要做一杯奶茶,步骤是固定的:煮茶→加奶→加糖→搅拌。无论做珍珠奶茶还是芋泥奶茶,步骤框架不变,只是某些细节不同(比如加珍珠或芋泥)。模板方法模式就是这个“奶茶配方”——它定义算法的骨架,将某些步骤的实现延迟到子类,使得子类可以不改变算法结构,却能调整具体步骤。


为什么需要它?

  • 代码复用:避免重复写相同流程的代码(比如文件读取的“打开→读取→关闭”)。
  • 控制扩展点:父类控制整体流程,子类只实现差异化步骤。
  • 减少冗余:多个子类共享同一套算法逻辑。

Java代码示例

假设我们需要实现一个“字符显示”和“字符串显示”的功能,两者显示流程相同(打开→打印→关闭),但具体实现不同:

1. 抽象父类:AbstractDisplay(定义算法骨架)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class AbstractDisplay {
// 模板方法:定义固定的流程(不可被子类重写)
public final void display() {
open();
for (int i = 0; i < 5; i++) { // 打印5次
print();
}
close();
}

// 抽象方法:由子类实现具体步骤
protected abstract void open();
protected abstract void print();
protected abstract void close();
}
2. 具体子类1:CharDisplay(显示单个字符)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class CharDisplay extends AbstractDisplay {
private char ch;

public CharDisplay(char ch) {
this.ch = ch;
}

@Override
protected void open() {
System.out.print("<<"); // 开头符号
}

@Override
protected void print() {
System.out.print(ch); // 重复打印字符
}

@Override
protected void close() {
System.out.println(">>"); // 结尾符号
}
}
3. 具体子类2:StringDisplay(显示字符串)
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
public class StringDisplay extends AbstractDisplay {
private String str;
private int width;

public StringDisplay(String str) {
this.str = str;
this.width = str.getBytes().length; // 计算字符串宽度
}

@Override
protected void open() {
printLine(); // 打印上边框
}

@Override
protected void print() {
System.out.println("|" + str + "|"); // 带边框的字符串
}

@Override
protected void close() {
printLine(); // 打印下边框
}

private void printLine() {
System.out.print("+");
for (int i = 0; i < width; i++) {
System.out.print("-");
}
System.out.println("+");
}
}
4. 客户端:Main(调用模板方法)
1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
// 显示字符'A'
AbstractDisplay d1 = new CharDisplay('A');
d1.display();

// 显示字符串"Hello"
AbstractDisplay d2 = new StringDisplay("Hello");
d2.display();
}
}

输出结果

1
2
3
4
5
6
7
8
<<AAAAA>>
+-----+
|Hello|
|Hello|
|Hello|
|Hello|
|Hello|
+-----+

模板方法模式的核心

  • 模板方法(display():定义算法流程(用final禁止子类重写)。
  • 抽象方法(open(), print(), close():子类必须实现的扩展点。
  • 钩子方法(可选):父类提供默认实现,子类可选择是否覆盖。

实际应用

  • JUnit测试框架setUp()tearDown()是模板方法中的钩子。
  • Servlet生命周期service()方法调用doGet()doPost()
  • Android的ActivityonCreate(), onStart()等构成生命周期模板。

优缺点

优点

  • 避免重复代码:公共逻辑抽取到父类。
  • 提高扩展性:新增功能只需增加子类。

缺点

  • 父类膨胀:若算法步骤过多,父类会变得复杂。
  • 继承强耦合:子类必须继承父类,无法通过组合灵活替换。

与策略模式的区别

  • 模板方法:用继承(父类控制流程,子类实现细节)。
  • 策略模式:用组合(将整个算法委托给策略对象)。

一句话总结

模板方法模式是“标准化流水线”——你负责定制零件,我来组装!