一、模板模式
模板模式又叫模板方法模式,在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。
在讲模板模式模式之前 先来看一个例子:
加工流程:
咖啡冲泡法:1.把水煮沸、2.用沸水冲泡咖啡、3.把咖啡倒进杯子、4.加糖和奶昔
茶冲泡法: 1.把水煮沸、2.用沸水冲泡茶叶、3.把 茶 倒进杯子、4.加柠檬
冲茶和冲咖啡的流程可以总结成下列模板:
把水煮沸—>泡饮料—>把饮料倒进杯子—>加调味料
二、实现
(1)抽象基类,为所有子类提供一个算法框架
package com.pattern.template;
/**
* 抽象基类,为所有子类提供一个算法框架
* 提神饮料
* @author fengjiang
*
*/
public abstract class RefreshBeverage {
/**
* 制备饮料的模板方法
* 封装了所有子类共同遵循的算法框架
*/
public final void prepareBeverageTemplate(){
//步骤一 将水煮沸
boilWater();
//步骤二 泡制饮料
brew();
//步骤三 将饮料倒入杯中
pourInCup();
//步骤四 加入调味料
addCondiments();
}
/*
* 基本方法:将水煮沸
*/
private void boilWater() {
System.out.println("将水煮沸");
}
/*
* 抽象方法:泡制饮料
*/
protected abstract void brew() ;
/*
* 基本方法 将饮料倒入杯中
*/
private void pourInCup() {
System.out.println("倒入杯中");
}
/*
* 抽象方法:加入调味料
*/
protected abstract void addCondiments() ;
}
package com.pattern.template;
/*
* 具体子类,提供了咖啡制备的具体实现
*/
public class Coffee extends RefreshBeverage {
/*
*
*
*/
@Override
protected void brew() {
System.out.println("泡制咖啡");
}
@Override
protected void addCondiments() {
System.out.println("加入糖和奶昔");
}
}
package com.pattern.template;
/*
* 具体子类,提供了茶制备的具体实现
*/
public class Tea extends RefreshBeverage {
@Override
protected void brew() {
System.out.println("泡制茶");
}
@Override
protected void addCondiments() {
System.out.println("加入柠檬");
}
}
package com.pattern.template;
public class RefreshBeverageTest {
public static void main(String[] args) {
/*
* 制备咖啡的过程
*/
System.out.println("制备咖啡");
RefreshBeverage b1=new Coffee();
b1.prepareBeverageTemplate();
System.out.println("咖啡好了");
System.out.println("-------------------");
/*
* 制备茶的过程
*/
System.out.println("制备茶");
RefreshBeverage b2=new Tea();
b2.prepareBeverageTemplate();
System.out.println("茶泡好了");
}
}
(5)测试结果:
制备咖啡
将水煮沸
泡制咖啡
倒入杯中
加入糖和奶昔
咖啡好了
-------------------
制备茶
将水煮沸
泡制茶
倒入杯中
加入柠檬
茶泡好了
三、钩子方法
一个钩子方法常常由抽象类给出一个空实现作为此方法的默认实现。这种空的钩子方法叫做“Do Nothing Hook”。显然,这种默认钩子方法在缺省适配模式里面已经见过了,一个缺省适配模式讲的是一个类为一个接口提供一个默认的空实现,从而使得缺省适配类的子类不必像实现接口那样必须给出所有方法的实现,因为通常一个具体类并不需要所有的方法。接着前面讲的泡茶的例子,比如现在有一位顾客现在不想在茶中加入柠檬,这时候我们就需要提供一个钩子方法先判断顾客是否需要加入调料。代码改动如下:
首先,改抽象基类:
package com.pattern.template;
/**
* 抽象基类,为所有子类提供一个算法框架
* 提神饮料
* @author fengjiang
*
*/
public abstract class RefreshBeverage {
/**
* 制备饮料的模板方法
* 封装了所有子类共同遵循的算法框架
*/
public final void prepareBeverageTemplate(){
//步骤一 将水煮沸
boilWater();
//步骤二 泡制饮料
brew();
//步骤三 将饮料倒入杯中
pourInCup();
if(<span style="color:#ff0000;">isCustomerWantsCondiments()</span>){
//步骤四 加入调味料
addCondiments();
}
}
/*
* Hook方法,钩子函数,提供一个默认和空得实现
* 具体的子类可以自行决定是否挂钩以及如何挂钩
* 询问用户是否加入调料
*/
protected boolean isCustomerWantsCondiments() {
return true;
}
/*
* 基本方法:将水煮沸
*/
private void boilWater() {
System.out.println("将水煮沸");
}
/*
* 抽象方法:泡制饮料
*/
protected abstract void brew() ;
/*
* 基本方法 将饮料倒入杯中
*/
private void pourInCup() {
System.out.println("倒入杯中");
}
/*
* 抽象方法:加入调味料
*/
protected abstract void addCondiments() ;
}
再来看茶的实现:
package com.pattern.template;
/*
* 具体子类,提供了茶制备的具体实现
*/
public class Tea extends RefreshBeverage {
@Override
protected void brew() {
System.out.println("泡制茶");
}
@Override
protected void addCondiments() {
System.out.println("加入柠檬");
}
@Override
/*
* 子类通过覆盖的形式选择挂载钩子函数,这样使用挂钩,让其决定里面的代码是否执行
*/
protected boolean isCustomerWantsCondiments() {
// TODO Auto-generated method stub
return false;
}
}
制备咖啡
将水煮沸
泡制咖啡
倒入杯中
加入糖和奶昔
咖啡好了
-------------------
制备茶
将水煮沸
泡制茶
倒入杯中
茶泡好了
对比前面的结果,“加入柠檬”这个过程没有了。
四、总结
1>模板模式定义了算法的步骤,把这些步骤的实现延迟到子类
2>模板模式为我们提供了一个代码复用的技巧
3>模板抽象类中可以定义具体方法、抽象方法和钩子方法
4>为了防止子类改变模板中的算法,可以将模板方法声明为final
5>钩子是一种方法,它在抽象类中不做事,或只做默认的事,子类可以选择要不要实现它