Template Method
什么是Template Method
Template Method的设计思想
简单的说,就是为了避免在多个子类实现某个算法的过程中,应用程序开发人员写错了算法过程,而导致产生了错误的结果。我们让类库开发人员自己写一个固定的算法骨架(于是此模式只能适应于算法骨架基本确定的应用场景!)。让一部分所有类都公用的算法、步骤,在类库中实现,然后让不同的应用程序开发人员书写适用于当前场景的部分步骤,然后调用父类的骨架方法,实现算法!
在Template Method设计模式中有哪些类型的方法
第一:交给类库实现的,应用程序开发人员不能修改的固定的方法
比如下面的实战1的银行业务办理程序,取号、业务结束给出评价,这两个步骤是不同的应用程序不需要管理的,于是设置为final的(在Java程序设计中是final,如果是c++程序设计则把函数实现写成const即可!)。这些程序往往是一些和任何应用程序开发者无关的代码,设置成final避免应用程序开发者无意中修改,导致了算法的结果错误!
第二:交给类库实现的,但是应用程序开发人员可根据实际情况,考虑修改或者不修改的
比如下面的实战2的数据分析程序,有些应用程序(子类)的应用场景下的数据,需要被加工再能被分析!但是有些数据是可以直接拿来分析,比如:Excel的数据往往可以在download后直接拿来处理,但是一些柱状图制类的数据需要二次加工后再处理!所以我们可以设置一个《钩子函数》的hook方法,这种方法一般是返回布尔值的(boolean/bool)。如果大多数情况是需要处理的,我们就给这个hook方法默认返回一个false,避免有些应用程序开发者忘记处理而进入数据分析的流程!如果当前的数据是不需要二次加工的,则拓展的子类重写该方法,返回true即可!
第三:交给应用程序开发者实现的,在抽象类中不管的抽象方法
抽象方法在Java中,以关键字abstract来体现,有abstract标注的方法的类,称之为抽象类。如果是在c++中,则是设置纯虚函数,有纯虚函数的类是抽象类。这个方法我们不能拿来实现,只需要在父类中定义,然后编译器会强制要去应用程序的开发者去根据自己的实际情况来实现抽象类。起到在运行期多态的效果!
使用 Template Method的好处
让算法固定,达到一种父类去调用子类的效果,符合《开闭原则》!
又利用继承与多态,达到算法骨架的代码复用的效果!
模板方法相关实战1:银行业务办理程序
设计分析:
在银行办理业务这个算法流程(骨架)中,有一下几个步骤:
① 取号
② 办理业务
③ 业务结束,给出本次业务的评价
其中有一些过程(方法)是静态的,就是取号、业务结束,这两个步骤。但是办理业务的过程是动态的,不同的用户角色,想办理不同的业务!所以我们只能把这个动态的过程(方法)抽象化,而固定不变的算法骨架。让不同的需要办理业务的角色类去继承这个抽象类,然后在子类中重写需要办理的业务,在重写结束后,再在调用的主类中去调用整个算法骨架process()。根据多态性,会根据子类的不同、实现的办理业务的程序的不同,在相同的算法骨架中得到不同的结果!
我的抽象父类:BankTranscation
java
/**
* 这个抽象类作为模板方法的父类,完成银行业务
* @author 不会编程的小良
* @version 1.0
* */
abstract class BaseBankTranscation {
static int ServerId = 1;
public void takeNumber(){
System.out.println("排队取号ing......");
}
/**
* 这个抽象函数负责由子类根据具体业务完成重写
* @author 不会编程的小良
* @version 1.0
* */
public abstract void transcation();
public void evalue(){
System.out.println("第 " + ServerId + " 次服务结束,完成评分......");
}
public final void process(){
System.out.println("开始第 " + ServerId + " 次服务!");
takeNumber();
transcation();
evalue();
ServerId++;
}
}
不同业务办理的子类
java
/**
* 这个子类负责重写父类BaseBankTranscation的方法transcation(),完成取款业务
* @author 不会编程的小良
* @version 1.0
* */
class Deposite extends BaseBankTranscation {
@Override
public void transcation() {
System.out.println("我要取款......");
}
}
/**
* 这个子类负责重写父类BaseBankTranscation的方法transcation(),完成存款业务
* @author 不会编程的小良
* @version 1.0
* */
class Withdraw extends BaseBankTranscation {
@Override
public void transcation() {
System.out.println("我要存款......");
}
}
/**
* 这个子类负责重写父类BaseBankTranscation的方法transcation(),完成转账业务
* @author 不会编程的小良
* @version 1.0
* */
class Transfer extends BaseBankTranscation {
@Override
public void transcation() {
System.out.println("我要转账......");
}
}
主类调用各个子类的process方法,完成业务办理
java
/**
* 这个主类负责完成各种银行业务的办理过程
* @author 不会编程的小良
* @version 1.0
* */
public class TemplateMethod {
public static void main(String[] args){
int n = 3;
BaseBankTranscation[] tran = new BaseBankTranscation[n];
for(int i = 0;i < n;++i){
switch (i % 3){
case 0:
tran[i] = new Deposite();
break;
case 1:
tran[i] = new Withdraw();
break;
case 2:
tran[i] = new Transfer();
break;
default:
break;
}
}
for(BaseBankTranscation item : tran) {
item.process();
}
}
}
执行的结果是:

模板方法相关实战2:数据处理与分析案例
设计分析:
这个数据处理与分析分成几个步骤:
① 数据获取(应用程序完成的抽象方法)
② 数据转换成可处理的模式 (hook方法)
③ 数据分析(应用程序完成的抽象方法)
④ 数据分析的结果展示(hook方法)
父类BaseDataAnalysis的实现
java
abstract class BaseDataAnalysis {
abstract void getData();
void connvertData(){
System.out.println("默认的数据转换方法......");
}
final void analysisData(){
System.out.println("完成数据分析工作......");
}
void displayData(){
System.out.println("默认的输出方式展示......");
}
boolean isValid(){
return true;
}
void process(){
getData();
if(isValid()){
connvertData();
}
analysisData();
displayData();
}
}
拓展出来的子类SubClass实现
java
class SubClass extends BaseDataAnalysis {
@Override
void getData(){
System.out.println("从JSON文件中获取数据......");
}
@Override
void displayData(){
System.out.println("以饼状图展示的方法展示......");
}
}
主类的测试方法执行
java
public class TemplateMethod {
public static void main(String[] args){
BaseDataAnalysis sub = new SubClass();
sub.process();
}
}
执行的结果是:

思考与练习
问题描述:
某银行软件的利息计算流程如下:
① 系统根据账户查询用户信息
② 根据用户信息来判断用户类型
③ 不同类型的用户使用不同的利息计算方式(如活期、定期)
④ 显示利息
我的解决方案------抽象银行利息类:
java
abstract class BankInterest {
boolean isValidUser(String username, String password){
if("jiangzhengliang".equals(username) && "123456".equals(password)) {
System.out.println("账户:" + username);
System.out.println("密码:" + password);
return true;
}
else {
return false;
}
}
abstract void getAccountMethod();
final void display(){
System.out.println("显示利息......");
}
void process(String name, String psswd){
if(isValidUser(name, psswd)){
getAccountMethod();
display();
}
}
}
我的解决方案------拓展的活期、定期利息子类
java
class CurrentAccount extends BankInterest {
@Override
void getAccountMethod() {
System.out.println("我是活期贷款......");
}
}
class SavingAccount extends BankInterest {
@Override
void getAccountMethod() {
System.out.println("我是定期贷款......");
}
}
我的解决方案------测试主类
java
public class TemplateMethod {
public static void main(String[] args){
BankInterest bank1 = new CurrentAccount();
bank1.process("jiangzhengliang", "123456");
BankInterest bank2 = new SavingAccount();
bank2.process("jiangzhengliang", "123456");
}
}
测试的结果:
