它的定义为:定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。说的通俗一点,就是为子类设计一个模板以便于子类复用里面的方法。为了避免子类恶意修改方法的实现细节,一般模板方法模式都会在方法上加final。
我们以泡茶和冲咖啡为例子。
步骤1 将水煮沸
boilWater();
//步骤2 泡制饮料(加茶叶或者咖啡)
brew();
//步骤3 将饮料倒入杯中
pourInCup();
if(isCustomerWantsCondiments()){
//步骤4 加入调味料(这个步骤可有可无,可以加柠檬,也可以加牛奶等)
可以看到步骤1 3是相同的,而2不相同,4是可有可无
看下代码
/*
* 抽象基类,为所有子类提供一个算法框架
*
* 提神饮料
*/
public abstract class RefreshBeverage {
/*
* 制备饮料的模板方法
* 封装了所有子类共同遵循的算法框架
*/
public final void prepareBeverageTemplate(){
//步骤1 将水煮沸
boilWater();
//步骤2 泡制饮料
brew();
//步骤3 将饮料倒入杯中
pourInCup();
if(isCustomerWantsCondiments()){
//步骤4 加入调味料
addCondiments();
}
}
/*
* Hook, 钩子函数,提供一个默认或空的实现
* 具体的子类可以自行决定是否挂钩以及如何挂钩
* 询问用户是否加入调料
*/
protected boolean isCustomerWantsCondiments() {
return true;
}
/*
* 基本方法,将水煮沸
*/
private void boilWater() {
System.out.println("将水煮沸");
}
/*
* 基本方法,将饮料倒入杯中
*/
private void pourInCup() {
System.out.println("将饮料倒入杯中");
}
/*
* 抽象的基本方法,泡制饮料
*/
protected abstract void brew();
/*
* 抽象的基本方法, 加入调味料
*/
protected abstract void addCondiments();
}
prepareBeverageTemplate就是模板算法,是final.因为模板是不可以随意改变的,所以子类不能改变它。
因为boilWater() & pourInCup在子类中都是相同的,所以设置为private
其他方法在子类中不同,所以设置为protected 子类才能重写它。
另外有个
protected boolean isCustomerWantsCondiments() {
return true;
}
这叫钩子函数,因为我们不知道第四个步骤到底要不要加。如果要,那就像钩子一样把第四个方法勾住它。
如果不要那就覆写它
protected boolean isCustomerWantsCondiments(){
return false;
}
这样在
if(isCustomerWantsCondiments()){
//步骤4 加入调味料
addCondiments();
}
中返回就是 false
来看看子类继承
package com.imooc.pattern.template;
/*
* 具体子类,提供了咖啡制备的具体实现
*/
public class Coffee extends RefreshBeverage {
@Override
protected void brew() {
System.out.println("用沸水冲泡咖啡");
}
@Override
protected void addCondiments() {
System.out.println("加入糖和牛奶");
}
}
没有覆写isCustomerWantsCondiments 那就默认要使用第四个方法
/*
* 具体子类,提供了制备茶的具体实现
*/
public class Tea extends RefreshBeverage {
@Override
protected void brew() {
System.out.println("用80度的热水浸泡茶叶5分钟");
}
@Override
protected void addCondiments() {
System.out.println("加入柠檬");
}
@Override
/*
* 子类通过覆盖的形式选择挂载钩子函数
* @see com.imooc.pattern.template.RefreshBeverage#isCustomerWantsCondiments()
*/
protected boolean isCustomerWantsCondiments(){
return false;
}
}
可以看到,它不需要第四个方法
最后看下测试代码
public class RefreshBeverageTest {
public static void main(String[] args) {
System.out.println("制备咖啡...");
RefreshBeverage b1 = new Coffee();
b1.prepareBeverageTemplate();
System.out.println("咖啡好了!");
System.out.println("\n******************************************");
System.out.println("制备茶...");
RefreshBeverage b2 = new Tea();
b2.prepareBeverageTemplate();
System.out.println("茶好了!");
}
}