模板方法模式

​ 模板方法模式是编程中经常用到的模式。它定义了一个操作中的算法骨架,将某些步骤延迟到子类中实现。这样,新的子类可以在不改变一个算法结构的前提下重新定义该算法的某些特定步骤。

场景

当一个客户到银行办理业务时,有以下流程:

  1. 取号排队
  2. 办理具体现金/转账/企业/理财业务
  3. 给银行工作人员评分

这时办理业务的一个标准流程,其中一三步是确定的,而第二步往往并不确定只有等到到窗口办理业务时才能知道到底要办什么业务。

根据以上场景可以总结出:
  模板方法模式,即处理某个流程的代码已经都具备,但是其中某个节点的代码暂时不能确定。因此采用模板方法模式,将这个节点的代码实现转移给子类去完成。即:处理步骤父类已经定义好了,具体实现延迟到子类中去定义。

模式结构

结构图

图1

  • AbstractClass抽象类:定义了算法的流程骨架,并实现了模板方法。
  • ConncreteClass具体类:实现抽象类,重现待实现的方法完成具体流程。

代码

AbstractClass抽象类

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 designMode.ModeMethod;

import java.util.Random;

/**
* @author YoungDream
* @since 2019/4/18 10:33
*/
public abstract class BankTmplateMethod {
private static int number = 0;

void takeNumber() {
System.out.println("取号:" + number++ + "号,开始排队");
}

abstract void transact();//办理具体的业务,钩子方法

void evaluate() {
System.out.println("反馈评分:" + new Random().nextInt(11) + "分");
}

//模板方法
public final void process() {
this.takeNumber();
this.transact();
this.evaluate();
}
}

ConncreteClass具体类

1
2
3
4
5
6
public class TakeMoney extends BankTmplateMethod {
@Override
void transact() {
System.out.println("取走现金:" + 100 + new Random().nextInt(100) + "元");
}
}
1
2
3
4
5
6
public class MoveMoney extends BankTmplateMethod {
@Override
void transact() {
System.out.println("转账给他人:" + 1 + new Random().nextInt(100) + "元");
}
}

客户端

1
2
3
4
5
6
7
8
9
public class Client {
public static void main(String[] args) {
BankTmplateMethod takeMoney = new TakeMoney();
BankTmplateMethod moveMoney = new MoveMoney();

takeMoney.process();
moveMoney.process();
}
}

控制台

1
2
3
4
5
6
7
取号:0号,开始排队
取走现金:10045元
反馈评分:8分

取号:1号,开始排队
转账给他人:126元
反馈评分:6分

上述回调方法又称为钩子方法:

满足好莱坞原则:“Don’t call me,We’ll call you”,在软件开发中,我们可以将call翻译为调用。子类不能调用父类,而通过父类调用子类。这些调用步骤已经在父类中写好了,完全由父类控制整个过程。

总结

使用场景

实现一个算法是,整体步骤很固定。但是,某些部分易变。易变部分可以抽象出来,供子类去实现。

优点

  • 模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码。
  • 子类实现算法的某些细节,有助于算法的扩展。
  • 通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开放-封闭原则”。

缺点

每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象。