模板方法模式 Template Method
根据实际案例解释模板方法模式
- 案例: 生产不同口味的豆浆,现有"红豆",“黄豆”,"绿豆"三种不同口味,虽然口味不同但是生产豆浆的步骤是相同的,选择豆子- - ->添加配料- - - >浸泡豆子- - ->打碎生成豆浆
- 模板方法模式属于行为型模式,将某个功能的实现步骤骨架定义到一个公共的父类模板方法中,将每个步骤的细节延迟到子类,根据需求进行具体实现,可以更改步骤中的细节,但整体流程不可以更改(好像刚好与建造者模式反过来)
代码示例
生产豆浆案例
- 抽象出公共父类,根据功能实现步骤的执行流程定义模板方法,提供抽象或默认的每个步骤的方法,供具体产品子类继承,重写
abstract class Production{
//模板方法(由于生产豆浆的流程步骤相同)
//根据执行流程定义模板方法,客户端生产豆浆时
//只需要调用模板方法即可
public void productionRun() {
select();
add();
soak();
broken();
}
//每个步骤的细节(如果不同种类的豆浆在当前某个步骤中操作
//方式一样则提供默认方法即可,若不一样,提供抽象方法
//子类根据需求进行重写
//选择豆子
public void select() {
System.out.println("选择当季豆子作为原材料");
};
//调价调料
public void add() {
System.out.println("向豆子中添加调料");
};
//浸泡
abstract void soak();
//打碎生成豆浆
abstract void broken();
}
- 创建具体产品子类,子类继承抽象父类,根据每个步骤的细节不同重写父类中的方法
//黄豆
class YelloBean extends Production{
//重写浸泡步骤
@Override
public void soak() {
System.out.println("黄豆需要浸泡三个小时");
}
//重写打碎步骤
@Override
public void broken() {
System.out.println("打碎黄豆需要10分钟");
}
}
//红豆
class RedBean extends Production{
@Override
public void soak() {
System.out.println("红豆需要浸泡两个小时");
}
@Override
public void broken() {
System.out.println("打碎红豆需要5分钟");
}
}
- 客户端调用测试
public class Test {
public static void main(String[]args) {
//生产黄豆豆浆
Production yProduction = new YelloBean();
//调用模板方法
yProduction.productionRun();
//生产红豆豆浆
Production rProduction = new RedBean();
rProduction.productionRun();
}
}
模板方法中的钩子方法
假设定义的模板方法中,的某个步骤,在某些产品中不执行,在模板类中添加返回Boolean值的钩子方法,具体产品子类根据需求实现钩子方法,返回真假,在模板方法中通过钩子方法判断是否执行某个步骤
- 定义了模板的抽血父类(只关注钩子方法)
abstract class Production{
//模板方法(由于生产豆浆的流程步骤相同)
//根据执行流程定义模板方法,客户端生产豆浆时
//只需要调用模板方法即可
public void productionRun() {
select();
//通过钩子方法来确定是否执行某个步骤
if(customerWantAdd()) {
add();
}
soak();
broken();
}
//每个步骤的细节(如果不同种类的豆浆在当前某个步骤中操作
//方式一样则提供默认方法即可,若不一样,提供抽象方法
//子类根据需求进行重写
//选择豆子
public void select() {
System.out.println("选择当季豆子作为原材料");
};
//调价调料
public void add() {
System.out.println("向豆子中添加调料");
};
//浸泡
abstract void soak();
//打碎生成豆浆
abstract void broken();
//返回布尔值的钩子方法,通过返回的真假确定模板方法中的某个步骤是否执行
abstract Boolean customerWantAdd();
}
- 具体产品
//黄豆
class YelloBean extends Production{
@Override
public void soak() {
System.out.println("黄豆需要浸泡三个小时");
}
@Override
public void broken() {
System.out.println("打碎黄豆需要10分钟");
}
//重写钩子方法返回true
@Override
Boolean customerWantAdd() {
return true;
}
}
不添加调料的纯豆浆(关注钩子方法)
//纯豆浆,不添加糖
class PureBean extends Production{
@Override
public void soak() {
System.out.println("纯豆浆黄豆需要浸泡三个小时");
}
@Override
public void broken() {
System.out.println("纯豆浆打碎黄豆需要10分钟");
}
//重写钩子方法返回 false
@Override
public Boolean customerWantAdd() {
return false;
}
}
- 调用时,如果生产纯豆浆,在执行模板方法时,调用纯豆浆里面重写的钩子方法,返回为false,模板方法中则不执行add()方法,另外抽象父类中提供的空方法,供子类根据需求,选择是否重写实现,这种也可以称为钩子方法
Spring 中模板方法的使用案例
在 Spring IOC 容器初始化用到了模板方法模式,查看 ConfigurableApplicationContext 接口 中的 refresh() 方法,继续向下查看该接口的实现类AbstractApplicationContext,中重写了 refresh() 方法,该方法就是一个模板方法,而AbstractApplicationContext就是一个公共的抽象父类,该类中定义了抽象方法,供子类实现,在refresh()方法中执行模板方法时调用的某些方法,就是通过子类实现例如下面的子类ClassPathXmlApplicationContext