参考文献

模版方法模式

  • 模板方法使用子类重写的抽象操作在基类中定义算法以提供具体行为.
    • 定义操作中算法的骨架,将某些步骤推迟到子类中.
    • 模板方法允许子类重新定义算法的某些步骤,而不让它们改变算法的结构.
  • 模板方法模式建议将算法分解为一系列步骤, 然后将这些步骤改写为方法, 最后在 “模板方法” 中依次调用这些方法. 步骤可以是 抽象的, 也可以有一些默认的实现. 为了能够使用算法, 客户端需要自行提供子类并实现所有的抽象步骤. 如有必要还需重写一些步骤 (但这一步中不包括模板方法自身).

组件

  • AbstractClass(抽象类): 定义具体子类定义的抽象基元操作以实现算法的步骤.
    • 实现定义算法骨架的模板方法.模板方法调用原始操作以及 AbstractClass 或其他对象中定义的操作
  • ConcreteClass(具体类): 实现原始操作以执行算法的子类特定步骤.
  • 当调用具体类时,将从基类执行模板方法代码,而对于模板方法内使用的每个方法,将从派生类调用其实现.

实现方式

  • 分析目标算法:首先,需要分析目标算法,并确定是否可以将其分解为多个步骤.如果算法可以被分解为多个具体步骤,那么就可以使用模版方法模式.
  • 找出通用步骤:接下来,从所有子类的角度出发,考虑哪些步骤能够通用,哪些步骤各不相同.将这些通用步骤定义为父类中的抽象方法.
  • 创建抽象基类:根据上一步骤中找出的通用步骤,创建一个抽象基类,并声明一个模板方法.抽象基类中的模板方法定义了算法的基本骨架,并按照一定的顺序调用抽象方法.
  • 实现具体步骤:在抽象基类中定义的抽象方法表示目标算法的各个步骤,每个具体子类必须实现这些抽象方法.子类可以根据自身需求选择重写部分可选步骤.
  • 添加钩子(Optional):在模板方法中的关键步骤之间可以添加钩子.钩子是一种可选步骤,允许子类对算法进行微调或扩展.
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// 步骤1:创建抽象基类
abstract class Algorithm {
// 模板方法定义算法的基本骨架
public final void execute() {
step1();
step2();
step3();
hook(); // 可选钩子方法
}

// 抽象方法,由子类实现具体步骤
protected abstract void step1();

protected abstract void step2();

protected abstract void step3();

// 钩子方法,子类可选择性重写
protected void hook() {
// 默认实现为空
}
}

// 步骤2:创建具体子类
class ConcreteAlgorithmA extends Algorithm {
@Override
protected void step1() {
System.out.println("执行算法A - 步骤1");
}

@Override
protected void step2() {
System.out.println("执行算法A - 步骤2");
}

@Override
protected void step3() {
System.out.println("执行算法A - 步骤3");
}
}

class ConcreteAlgorithmB extends Algorithm {
@Override
protected void step1() {
System.out.println("执行算法B - 步骤1");
}

@Override
protected void step2() {
System.out.println("执行算法B - 步骤2");
}

@Override
protected void step3() {
System.out.println("执行算法B - 步骤3");
}

@Override
protected void hook() {
System.out.println("算法B的钩子方法");
}
}

// 步骤3:在客户端代码中使用模板方法
public class TemplateMethodExample {
public static void main(String[] args) {
Algorithm algorithmA = new ConcreteAlgorithmA();
algorithmA.execute();

System.out.println();

Algorithm algorithmB = new ConcreteAlgorithmB();
algorithmB.execute();
}
}

使用场景

  • 一次性实现算法的不变部分,然后将其留给子类来实现可能变化的行为.
  • 当执行重构并识别类之间的共同行为时.应创建一个包含所有公共代码(在模板方法中)的抽象基类以避免代码重复

实现注意点

Minimizing primitive methods number(最小化原始方法数量)

  • 在设计模板方法时,重要的是要尽量减少子类必须重写的原始方法的数量,以便提供清晰、简单的方法来实现具体模板.

Naming Convetions(命名约定)

  • 为了识别原始方法,最好使用特定的命名约定.例如,前缀do可用于原始方法.以类似的方式,自定义挂钩可以具有prepost等前缀.