定义理解
-
模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
-
模板方法模式是一种行为设计模式,它定义了一个操作中的算法的骨架,并将一些步骤延迟到子类中实现。这样,模板方法可以让子类在不改变算法结构的前提下,重新定义算法的某些特定步骤。
结构
模板方法模式通常包含以下两个角色:
- 抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它定义了一个或多个模板方法,这些模板方法通常是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。
- 具体类(Concrete Class):实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤。
优点
- 封装不变部分,扩展可变部分:把认为是不变部分的算法封装到父类实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
- 提取公共代码,便于维护:提取了类库中的公共行为,将公共行为放在父类中,而通过其子类来实现不同的行为。通过一个父类调用其子类的操作,通过子类扩展增加新的行为,符合“开闭原则”。
- 行为由父类控制,子类实现:基本方法是由子类实现的,因此子类可以通过扩展的方式增加相应的功能,符合“开闭原则”。
缺点
- 每个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,导致一种反向的控制结构,它提高了代码阅读的难度。
模板方法模式的使用场景
- 多个子类有公有的方法,并且逻辑基本相同时。
- 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
- 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为。
场景示例
在银行办理业务时,通常包含几个基本固定的步骤:取号排队、办理具体业务、对银行工作人员进行评分。其中,取号排队和对银行工作人员进行评分的业务逻辑对于所有业务来说都是一样的,但办理具体业务的部分则根据业务类型(如取款、存款、转账等)而有所不同。
抽象类(AbstractClass)
定义一个抽象类AbstractBank,这个类包含了银行业务办理流程的骨架,即模板方法。同时,它还定义了一些抽象方法和具体方法,以及一些钩子方法。
abstract class AbstractBank {
// 模板方法,定义了业务办理的骨架流程
public final void templateMethodProcess() {
this.takeNumber(); // 取号排队
this.transact(); // 办理具体业务(抽象方法,由子类实现)
if (this.isEvaluateHook()) { // 判断是否需要评分(钩子方法,默认返回true)
this.evaluateHook(); // 对银行工作人员进行评分(钩子方法,提供默认实现)
}
}
// 具体方法:取号排队
private void takeNumber() {
// 实现取号排队的逻辑
System.out.println("Take a number and wait for your turn.");
}
// 抽象方法:办理具体业务,由子类实现
protected abstract void transact();
// 钩子方法:对银行工作人员进行评分,提供默认实现
protected void evaluateHook() {
System.out.println("Please evaluate the service.");
}
// 钩子方法:判断是否需要评分,默认返回true
protected boolean isEvaluateHook() {
return true;
}
}
具体类(ConcreteClass)
对于不同的业务类型(如取款、存款、转账),可以定义不同的具体类来继承AbstractBank类,并实现transact方法(如果需要的话,还可以重写钩子方法)。
// 具体类:存款
class ConcreteDeposit extends AbstractBank {
@Override
protected void transact() {
// 实现存款业务的逻辑
System.out.println("Deposit money.");
}
}
// 具体类:取款
class ConcreteWithdraw extends AbstractBank {
@Override
protected void transact() {
// 实现取款业务的逻辑
System.out.println("Withdraw money.");
}
}
// 具体类:转账
class ConcreteTransfer extends AbstractBank {
@Override
protected void transact() {
// 实现转账业务的逻辑
System.out.println("Transfer money.");
}
}
使用示例
现在,当需要办理转账业务时,只需要创建ConcreteTransfer类的实例,并调用其templateMethodProcess方法即可。这个方法会自动按照模板方法中定义的流程来执行,其中办理具体业务的步骤则由ConcreteTransfer类提供。
public class BankDemo {
public static void main(String[] args) {
ConcreteTransfer transfer = new ConcreteTransfer();
transfer.templateMethodProcess();
// 输出:
// Take a number and wait for your turn.
// Transfer money.
// Please evaluate the service.
}
}