模板方法 Template Method

模板方法模式是一种行为设计模式,它在超类中定义了一个算法的框架,允许子类在不修改结构的情况下重写算法的特定步骤。

🔖 通常是对算法的特定步骤进行优化,而不是对整个算法进行修改。


为什么要使用?

模板方法模式的对象职责:

在父类中定义处理流程的框架,在子类中实现具体处理。

👉 期望在一个通用的算法或流程框架下进行自定义开发。

👉 避免同样的代码逻辑进行重复编码。

💡 模板方法模式将通用操作和流程处理交给父类处理,其他具体操作交由子类实现。


模式结构

  • 抽象类(Abstract Class)会声明作为算法步骤的方法,以及依次调用它们的实际模板方法。算法步骤可以被声明为 abstract 类型,也可以提供一些默认实现。
  • 具体类(Concrete ­Class)可以重写所有步骤,但不能重写模板方法自身。

模板方法模式的类图:


模式实现

该示例使用模板方法模式将 display 操作交由父类实现,其他操作由子类(显示单个字符的类 CharDisplay 和显示整个字符串的类 StringDisplay )实现,display 主要负责控制 openprintclose 的调用顺序。

示例程序的类图

代码实现

抽象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package example;

/** 抽象模板类 */
public abstract class AbstractDisplay {
/** 子类需要实现的方法 */
public abstract void open();

/** 子类需要实现的方法 */
public abstract void print();

/** 子类需要实现的方法 */
public abstract void close();

/** 模板方法,处理流程 */
public final void display() {
open();
print();
close();
}
}
具体类
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
package example;

/** 具体子类,字符方式打印 */
public class CharDisplay extends AbstractDisplay {
private char ch;

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

/** 子类需要实现的方法 */
@Override
public void open() {
System.out.print("<<");
}

/** 子类需要实现的方法 */
@Override
public void print() {
System.out.print(ch);
}

/** 子类需要实现的方法 */
@Override
public void close() {
System.out.println(">>");
}
}
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
39
40
41
42
43
44
45
46
47
48
49
50
51
package example;

/** 具体子类,字符串方式打印 */
public class StringDisplay extends AbstractDisplay {
private String str;

public StringDisplay(String str) {
this.str = str;
}

/** 子类需要实现的方法 */
@Override
public void open() {
System.out.print("+");
for (int i = 0; i < str.length(); i++) {
System.out.print("-");
}
System.out.println("+");
}

/** 子类需要实现的方法 */
@Override
public void print() {
printLine();
System.out.print("|");
for (int i = 0; i < str.length(); i++) {
System.out.print(str.charAt(i));
}
System.out.println("|");
printLine();
}

/** 子类需要实现的方法 */
@Override
public void close() {
System.out.print("+");
for (int i = 0; i < str.length(); i++) {
System.out.print("-");
}
System.out.println("+");
}

/** 子类特有的私有方法 */
private void printLine() {
System.out.print("|");
for (int i = 0; i < str.length(); i++) {
System.out.print("-");
}
System.out.println("|");
}
}

代码测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import example.AbstractDisplay;
import example.CharDisplay;
import example.StringDisplay;

/** 测试模板方法模式 */
public class Test {
public static void main(String[] args) {
AbstractDisplay charDisplay = new CharDisplay('H');
AbstractDisplay stringDisplay = new StringDisplay("Hellovie");
System.out.println("CharDisplay实现");
charDisplay.display();

System.out.println("\n----------------- 分割线 -----------------\n");

System.out.println("StringDisplay实现");
stringDisplay.display();
}
}

输出结果

1
2
3
4
5
6
7
8
9
10
11
CharDisplay实现:
<<H>>

----------------- 分割线 -----------------

StringDisplay实现:
+--------+
|--------|
|Hellovie|
|--------|
+--------+

常用场景和解决方案

  • 多个类有相同的方法并且逻辑可以共用时。
  • 将通用的算法或固定流程设计为模板,在每一个具体的子类中再继续优化算法步骤或流程步骤时。
  • 重构超长代码时,发现某一个经常使用的共有方法。
  • 当你只希望客户端扩展某个特定算法步骤,而不是整个算法或其结构时。

模式的优缺点

优点 缺点
你可仅允许客户端重写一个大型算法中的特定部分,使得算法其他部分修改对其所造成的影响减小。 部分客户端可能会受到算法框架的限制。
你可将重复代码提取到一个超类中,有效去除重复代码。 通过子类抑制默认步骤实现可能会导致违反里氏替换原则
有助于找到更通用的模板。 模板方法中的步骤越多,其维护工作就可能会越困难。

拓展知识

  • 工厂方法模式是模板方法模式的一种特殊形式。同时,工厂方法可以作为一个大型模板方法中的一个步骤。
  • 继承的结构容易带来“修改一个类而影响所有类”的情况。


🔙 设计模式

📌最后:希望本文能够给您提供帮助,文章中有不懂或不正确的地方,请在下方评论区💬留言!

🔗参考文献:

🌐 设计模式 –refactoringguru

▶️ bilibili-趣学设计模式;黄靖锋. –拉勾教育

📖 图解设计模式 /(日)结城浩著;杨文轩译. –北京:人民邮电出版社,2017.1