1:概念和背景
模板方法(Template Method)模式定义了一个算法框架,并通过继承的方式将算法的实现延迟到子类中,使得子类可以在不改变算法框架及其流程的前提下重新定义该算法在某些特定环节的实现,是一种类行为型模式。
该模式在抽象类中定义了算法的结构并实现了公共部分算法,在子类中实现可变的部分并根据不同的业务需求实现不同的扩展。模板方法模式的优点在于其在父类(抽象类)中定义了算法的框架以保障算法的稳定性,同时在父类中实现了算法公共部分的方法来保障代码的复用;将部分算法部分延迟到子类中实现,因此子类可以通过继承的方式来扩展或重新定义算法的功能而不影响算法的稳定性,符合开闭原则。
模板方法模式需要注意抽象类与具体子类之间的协作,在具体使用时包含以下主要角色:
- 抽象类(Abstract Class):定义了算法的框架,由基本方法和模板方法组成。基本方法定义了算法有哪些环节,模板方法定义了算法各个环节执行的流程
- 具体子类(Concrete Class):对在抽象类中定义的算法根据需求进行不同的实现。
2:举例说明
下面以银行办理业务为例实现一个模板方法模式,我们去银行办理业务都要经过抽号、排队、办理业务和评价,其中的业务流程是固定的,但办理的具体业务比较多,比如取钱、存钱、开卡等。其中,办理业务的固定流程就是模板算法中的框架,它常常是不变的,由抽象类定义和实现,而具体办理的业务是可变的部分,通常交给子类去做具体的实现。具体的UML设计如图:
3:代码具体实现
①定义AbstractTemplate模板类:
/**
* 模板类
*
* @Author: ganbo
* @Date: 2020/6/12 14:31
*/
public abstract class AbstractTemplate {
/**
* 模板方法,用于核心流程和算法的定义
*/
public void templateMethod() {
checkNumber();
queueUp();
handleBusiness();
serviceEvaluation();
}
/**
* 抽号
*/
public void checkNumber() {
System.out.println("抽号。。。");
}
/**
* 排队
*/
public void queueUp() {
System.out.println("排队...");
}
/**
* 业务办理(具体办理什么业务下沉到具体的业务实现类中实现)
*/
public abstract void handleBusiness();
/**
* 服务评价
*/
public void serviceEvaluation() {
System.out.println("业务办理完毕,服务评价...");
}
}
以上代码定义了抽象类AbstractTemplate,用于实现模板方法模式,其中定义了checkNumber()表示抽号过程,queueUp()表示排队过程,handleBusiness()表示需要办理的具体业务,serviceEvaluation()表示在业务办理完成后对服务的评价,templateMethod()定义了银行办理业务的核心流程,即取号、排队、办理业务和评价。抽象类实现了取号、排队、办理业务这些公共方法,而将办理业务的具体方法交给具体的业务类实现。
②定义存钱业务实现:
/**
* 存钱业务
*
* @Author: ganbo
* @Date: 2020/6/12 14:36
*/
public class SaveMoney extends AbstractTemplate {
public void handleBusiness() {
System.out.println("办理存钱业务。");
}
}
以上代码定义了SaveMoney并实现了handleBusiness(),以完成存钱的业务逻辑。
③定义取钱的业务实现:
/**
* 取钱业务
*
* @Author: ganbo
* @Date: 2020/6/12 14:36
*/
public class TakeMoney extends AbstractTemplate {
public void handleBusiness() {
System.out.println("办理取钱业务。");
}
}
以上代码定义了TakeMoney并实现了handleBusiness(),以完成取钱的业务逻辑。
④使用模板模式:
/**
* 模板方法测试类
*
* @Author: ganbo
* @Date: 2020/6/12 14:38
*/
public class TemplateMethodPatternTest {
public static void main(String[] args) {
//存钱业务(使用多态)
AbstractTemplate saveMoney = new SaveMoney();
saveMoney.templateMethod();
//取钱业务(使用多态)
AbstractTemplate takeMoney = new TakeMoney();
takeMoney.templateMethod();
}
}
在使用模板模式时只需按照需求定义具体的模板类实例并调用其模板方法即可,具体的执行结果如下:
4:总结和应用
优点:
- 封装不变,扩展可变:父类封装了具体流程以及实现部分不变行为,其它可变行为交由子类进行具体实现;
- 流程由父类控制,子类进行实现:框架流程由父类限定,子类无法更改;子类可以针对流程某些步骤进行具体实现;
缺点:
- 抽象规定了行为,具体负责实现,与通常事物的行为相反,会带来理解上的困难(通俗地说,“父类调用了子类方法”);
源码中的应用:
重构时, 模板方法模式是一个经常使用的模式,把相同的代码抽取到父类,然后通过钩子函数约束其行为;在很多框架源码中大量使用模板方法模式。把重要,复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现;
- Spring源码中的JdbcTemplate:比如里面的query方法需要传递一个RowMapper参数,该对象就需要用户自己去实现(通常是采用匿名内部类实现),并实现里面的mapRow方法,在JdbcTemplate中模板方法会调用子类具体实现的RowMapper中的mapRow方法。同时HibernateTemplate方法等,都是使用的模板方法模式。