模板方法模式(TemplateMethod)

First Post:

Last Update:

模板方法模式(TemplateMethod)

当我们要完成在某一细节层次一致的一个过程或一系列步骤在更详细的层次上的实现可能不同时,我们考虑用模板方法模式来处理。

模板方法模式,定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

总的来说,模板模式就是通过抽象类来定义一个逻辑模板,逻辑框架、逻辑原型,然后将无法决定的部分抽象成抽象类交由子类来实现,一般这些抽象类的调用逻辑还是在抽象类中完成,也就是说,算法的结构是被模板定义的。这么看来,模板就是定义一个框架,比如盖房子,我们定义一个模板:房子要封闭,有门,有窗等等,但是要什么样的门,什么样的窗,这些并不在模板中描述,这个交给子类来完善,比如门使用防盗门,窗使用北向的窗等等。

其中基本方法一般会用final修饰,保证其不会被子类修改,而模板方法则使用protected修饰,表明其需要在子类中实现。

其实,模板模式中还有一个钩子方法的概念,有人称,具有钩子方法的模板模式才算完整,也许吧。
钩子方法时干啥的呢?钩子就是给子类一个授权,允许子类通过重写钩子方法来颠覆基本逻辑的执行,这有时候是非常有用的。

模板方法模式结构图

模板方法模式代码结构

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
AbstractClass——抽象模板
包含了一个模板方法,(一般为一个具体方法)
给出了一个顶级逻辑的骨架,而逻辑的组成步骤是各个推迟到子类实现的抽象方法。
顶级逻辑也可以调用一些具体方法。

class AbstractClass
{
public abstract void PrimitiveOperation1( );
public abstract void PrimitiveOperation2( );
public void TemplateMethod( )
{
PrimitiveOperation1( );
PrimitiveOperation2( );
//do something;
}
}

ConcreteClass,具体逻辑实现类,实现父类所定义的一个或多个抽象方法。每个AbstractClass都可以由任意多个ConcreteClass与之对应,而每一个ConcreteClass都可以给出这些抽象方法(也就是这些顶级逻辑的任意组成步骤的不同实现),从而使得顶级逻辑的实现各不相同。

ConcreteClassA : AbstractClass
{
public override void PrimitiveOperation1( )
{//具体实现逻辑方法
//具体类A方法1实现
}
public override void PrimitiveOperation2( )
{//具体实现逻辑方法
//具体类A方法2实现
}
}

ConcreteClassB : AbstractClass
{
public override void PrimitiveOperation1( )
{//具体实现逻辑方法
//具体类B方法1实现
}
public override void PrimitiveOperation2( )
{//具体实现逻辑方法
//具体类B方法2实现
}
}

客户端调用
AbstractClass c;

c = new AbstractClassA( );
c.TemplateMethod( );

c = new AbstractClassB( );
c.TemplateMethod( );

模板方法模式例子——建房子

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
public abstract class HouseTemplate {
protected HouseTemplate(String name){
this.name = name;
}
protected String name;
protected abstract void buildDoor();//建门
protected abstract void buildWindow();//建窗户
protected abstract void buildWall();//建承重
protected abstract void buildBase();//建地基
protected abstract void buildToilet();//建厕所
//钩子方法
protected boolean isBuildToilet(){
return true;
}
//公共逻辑
public final void buildHouse(){//调用顺序确定
buildBase();
buildWall();
buildDoor();
buildWindow();
if(isBuildToilet()){
buildToilet();
}
}
}

public class HouseOne extends HouseTemplate {
HouseOne(String name){
super(name);
}
HouseOne(String name, boolean isBuildToilet){
this(name);
this.isBuildToilet = isBuildToilet;
}
public boolean isBuildToilet;
@Override
protected void buildDoor() {
System.out.println(name +"的门要采用防盗门");
}
@Override
protected void buildWindow() {
System.out.println(name + "的窗户要面向北方");
}
@Override
protected void buildWall() {
System.out.println(name + "的墙使用大理石建造");
}
@Override
protected void buildBase() {
System.out.println(name + "的地基使用钢铁地基");
}
@Override
protected void buildToilet() {
System.out.println(name + "的厕所建在东南角");
}
//重写钩子方法
@Override
protected boolean isBuildToilet(){
return isBuildToilet;
}
}

public class HouseTwo extends HouseTemplate {
HouseTwo(String name){
super(name);
}
@Override
protected void buildDoor() {
System.out.println(name + "的门采用木门");
}
@Override
protected void buildWindow() {
System.out.println(name + "的窗户要向南");
}
@Override
protected void buildWall() {
System.out.println(name + "的墙使用玻璃制造");
}
@Override
protected void buildBase() {
System.out.println(name + "的地基使用花岗岩");
}
@Override
protected void buildToilet() {
System.out.println(name + "的厕所建在西北角");
}
}

//测试类
public class Clienter {
public static void main(String[] args){
HouseTemplate houseOne = new HouseOne("房子1", false);
HouseTemplate houseTwo = new HouseTwo("房子2");
houseOne.buildHouse();
houseTwo.buildHouse();
}
}

测试结果:
房子1的地基使用钢铁地基
房子1的墙使用大理石建造
房子1的门要采用防盗门
房子1的窗户要面向北方
房子2的地基使用花岗岩
房子2的墙使用玻璃制造
房子2的门采用木门
房子2的窗户要向南
房子2的厕所建在西北角

模板方法总结

  1. 使用抽象类定义模板类,并在其中定义所有的基本方法、模板方法,钩子方法,不限数量,以实现功能逻辑为主。其中基本方法使用final修饰,其中要调用基本方法和钩子方法,基本方法和钩子方法可以使用protected修饰,表明可被子类修改。

  2. 定义实现抽象类的子类,重写其中的模板方法,甚至钩子方法,完善具体的逻辑。

模板方法使用场景

  1. 在多个子类中拥有相同的方法,而且逻辑相同时,可以将这些方法抽出来放到一个模板抽象类中。

  2. 程序主框架相同,细节不同的情况下,也可以使用模板方法。

模板方法模式的优势

  • 模板方法是通过把不变行为搬移到超类,去除子类中的重复代码来体现他的优势。

  • 模板方法模式就是提供了一个很好的代码复用平台。

  • 当不变的和可变的行为在方法的子类实现中混合在一起,不变的行为就会在子类中重复的出现,通过模板方法模式,可以将这些行为搬移到单一的地方,帮助这些子类摆脱重复的不变行为。