1、概述
模板方法模式是一种基于继承的代码复用的类行为型模式,是结构最简单的行为型设计模式,在其结构中只存在父类与子类之间的继承关系。
通过使用模板方法模式,可以将一些复杂流程的实现步骤封装在一系列基本方法中,在抽象父类中提供一个称之为“模板方法”的方法,来定义这些基本方法的执行次序,在其子类中覆盖某些步骤,使得相同的算法框架可以有不同的执行结果。模板方法模式提供了一个模板方法来定义算法框架,而某些具体步骤的实现可以在其子类中完成。
2、实例
考虑一个计算银行存款利息的例子。假设系统需要支持两种类型的存款账号,即货币市场账号和定期存款账号。这两种账号的存款利息是不同的,因此,在计算一个用户的存款利息金额时,必须区分这两种不同的账号类型。如何设计这个功能?
抽象父类的代码如下所示:
public abstract class Account {
/**
* 计算利息金额(模板方法)
* @return double 利息金额
*/
public final double calculateInterest() {
double interestRate = doCalculateInterestRate();
AccountType accountType = doCalculateAccountType();
double amount = calculateAmount(accountType);
return amount * interestRate;
}
/**
* 获取利率(基本方法,子类实现)
* @return double 利率
*/
abstract double doCalculateInterestRate();
/**
* 获取账号类型(基本方法,子类实现)
* @return AccountType 账号类型
*/
abstract AccountType doCalculateAccountType();
/**
* 根据账号类型获取本金(基本方法,已经实现)
*/
private double calculateAmount(AccountType accountType) {
if (AccountType.CERTIFICATE_OF_DEPOSITE == accountType) {
return 20000.00;
}
return 10000.00;
}
}
其中,账号类型采用枚举类型实现:
public enum AccountType {
/**
* 货币市场账号
*/
MONEY_MARKET,
/**
* 定期存款账号
*/
CERTIFICATE_OF_DEPOSITE;
}
子类的代码分别如下所示:
public class MoneyMarketAccount extends Account {
@Override
AccountType doCalculateAccountType() {
return AccountType.MONEY_MARKET;
}
@Override
double doCalculateInterestRate() {
return 0.05;
}
}
public class CDAccount extends Account {
@Override
AccountType doCalculateAccountType() {
return AccountType.CERTIFICATE_OF_DEPOSITE;
}
@Override
double doCalculateInterestRate() {
return 0.04;
}
}
3、优缺点
优点:
1、提高代码复用性:将相同部分的代码放在抽象的父类中。
2、提高代码拓展性:将不同的代码放入不同的子类中,通过对子类的扩展增加新的行为。
3、实现反向控制:通过一个父类调用其子类的操作,通过对子类的扩展增加新的行为,实现了反向控制且符合“开闭原则”。
缺点:
引入了抽象类,每一个不同的实现都需要一个子类来实现,导致类的个数增加,从而增加了系统实现的复杂度。
注意事项:
为防止恶意操作,一般模板方法都加上 final 关键词。
4、应用场景
1、多个子类有公有的方法,并且逻辑基本相同时;
2、重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现;
3、重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为;