模板方法模式

定义

定义了一个算法的骨架,并允许子类为一个或多个步骤提供实现;
模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

适用场景

  • 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现;
  • 各个子类中公共的行为被提取出来并集中到一个公共父类中,从而避免代码重复。

优点和缺点

优点

  • 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
  • 它在父类中提取了公共部分代码,便于代码复用;
  • 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

缺点

  • 对每个不同的实现都需要定义一个子类,这会导致类的数量增加,系统更加庞大,设计也更加抽象,间接增加的系统实现的复杂度。
  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码的阅读难度。
  • 由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都需要改一遍。

代码实现

假设一个场景,我们要生产电脑,每个厂家生产电脑配置的东西不一样

首先创建抽象类,限制创建电脑的模板

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
public abstract class AComputer {

/**
* 模板方法,声明成final是不希望子类覆盖这个类
*/
protected final void assembleComputer(){
//限制方法的调用,不让子类重写
principal();
displayer();
keyboard();
if (needMouse()){
mouse();
}
}

/**
* 生产主机,每个电脑都必须的,所以也声明成final
*/
protected final void principal(){
System.out.println("生产主机");
}

/**
* 显示器,每个电脑也都需要,所以也声明成final
*/
protected final void displayer(){
System.out.println("生产显示器");
}

/**
* 键盘配置,由子类来实现
*/
abstract void keyboard();

/**
* 鼠标为非必须品,可以通过钩子方法来判断是否需要
*/
protected final void mouse(){
System.out.println("生产鼠标");
}

/**
* 鼠标的钩子方法,默认返回false,如果子类需要,重写该方法即可
*/
protected boolean needMouse(){
return false;
}
}

创建子类,继承抽象类,实现抽像方法

1
2
3
4
5
6
7
8
9
public class AsusComputer extends AComputer{
/**
* 生产键盘
*/
@Override
void keyboard() {
System.out.println("华硕机械键盘");
}
}

子类重写钩子方法,控制父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class LenovoComputer extends AComputer{
@Override
void keyboard() {
System.out.println("联想键盘");
}

/**
* 联想生产键盘,同时也生产鼠标,重写鼠标钩子方法,返回true即可
*/
@Override
protected boolean needMouse() {
return true;
}
}

或者通过入参来控制父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class HpComputer extends AComputer{

private boolean needMouseFlag = false;

public HpComputer(boolean needMouseFlag) {
this.needMouseFlag = needMouseFlag;
}

/**
* 惠普生产键盘,但是鼠标可能生产可能不生产,可以将钩子的值作为变量通过构造函数设置
*/
@Override
void keyboard() {
System.out.println("惠普键盘");
}

@Override
protected boolean needMouse() {
return this.needMouseFlag;
}
}

UML类图