定义
介绍: 定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某项特定步骤。
核心: 处理某个流程的代码已经都具备,但是某个节点的代码暂时不能确定。因此我们使用模板方法模式,将这个节点的代码转移给子类来完成。即:处理步骤父类中定义好,具体实现延迟到子类中定义。
结构图
- AbstractClass是抽象类,其实也就是一个抽象模板,定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现,顶级逻辑也有可能调用一些具体方法。
- ConcreteClass:实现父类中定义的一个或多个抽象方法。每一个AbstractClass可以有任意多个ConcreteClass与之对应,而每一个ConcreteClass都可以给出这些抽象方法(也就是顶级逻辑的实现步骤)的不同实现,从而使得顶级逻辑的实现各不相同。
作用
当不变的和可变的行为在方法的子类混合在一起的时候,不变的行为就会在子类中重复出现,模板方法模式就是提供了一个很好的代码复用平台,通过把不变的行为迁移到超类,去除子类中重复代码来体现它的优势,这样就帮助子类摆脱了不变的重复行为的纠缠。
模拟场景
客户到银行办理业务:
- 取号排队
- 办理具体现金、转账、企业、个人、理财等业务
- 给银行工作人员评分
代码实现
- 模板抽象类,定义算法骨架,将可变业务延迟到子类实现
/**
* User:tumbler
* Desc:模板方法模式--抽象模板类定义
*/
public abstract class BankTemplateMethod {
//基本流程
/**
* 固定方法 取号排队
*/
public void takeNumber() {
System.out.println("1、取号排队");
}
/**
* 具体业务方法,办理具体的业务,延迟到子类实现,类似与钩子方法
*/
public abstract void transact();
/**
* 固定方法 反馈评分
*/
public void evaluate() {
System.out.println("3、反馈评分");
}
/**
* 定义算法骨架,一般不能由子类实现
* 模板方法:将基本操作组合到一起
*/
public final void process() {
this.takeNumber();
this.transact(); //像个钩子,执行时挂那个子类就执行那个子类的方法
this.evaluate();
}
}
- 定义具体实现类,大多数时候直接用匿名内部类来调用
/**
* User:tumbler
* Desc:模板方法模式--具体实现类-取钱业务
*/
public class DrawMoney extends BankTemplateMethod {
@Override
public void transact() {
System.out.println("办理业务:取一个亿!!!");
}
}
- 测试Client,包含匿名内部类的使用
/**
* User:tumbler
* Desc:模板方法模式--测试
*/
public class Client {
public static void main(String[] args) {
BankTemplateMethod btm1 = new DrawMoney();
btm1.process();
//使用匿名内部类
BankTemplateMethod btm2 = new BankTemplateMethod() {
@Override
public void transact() {
System.out.println("办理业务:存三毛钱!!!");
}
};
btm2.process();
BankTemplateMethod btm3 = new BankTemplateMethod() {
@Override
public void transact() {
System.out.println("办理业务:理财款3000万。。。");
}
};
btm3.process();
}
}
运行结果:
1、取号排队
办理业务:取一个亿!!!
3、反馈评分
1、取号排队
办理业务:存三毛钱!!!
3、反馈评分
1、取号排队
办理业务:理财款3000万。。。
3、反馈评分
从中可以看出,其实就是一个多态的体现,具体调用那个子类,就执行子类的相应方法。
另一称呼
方法回调(钩子方法)
-
好莱坞原则:Don’t call me,we’ll call you back!
在好莱坞,在艺人将简历交给好莱坞娱乐公司后,所能做的就是等待,整个过程由娱乐公司控制,演员只能被动的服务安排,在需要的时候再由公司安排出演。 -
在软件开发中,将call翻译为调用。子类不能调用父类,而通过父类调用子类。这些调用步骤已经在父类写好了,完全由父类控制整个过程。
何时使用及开发常见
- 实现一个算法时,整体步骤很固定,但是某些部分容易改变,易变部分可以抽象出来供子类实现。
- 数据库访问的封装
- Junit单元测试
- Servlet中关于doGet() 和 doPost()方法的调用
- Hibernate中模板的使用
- Spring中JDBCTemplate、HibernateTemplate等