1.什么是模板方法模式?
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。 模板方法模式使得子类可以在不改变算法结构的情况下重新定义算法的某些步骤。
2.根据实例的程序案例学习模板方法模式
2.1 简化重复的代码
现在我司的一个客户: 知名饮料制作商 觉得自己的饮料制作系统的源代码有些繁重了,想请我们帮他们进行优化一下。
员工A接到的任务是将Coffee,Tea这两个类进行代码简化:
Coffee.java
package TemplatePattern.first;
public class Coffee {
public void boilWater(){
System.out.println("Boiling water");
}
public void brewCoffeeGrinds(){
System.out.println("Dripping coffee through filter");
}
public void pourInCup(){
System.out.println("Pouring into cup");
}
public void addSugarAndMilk(){
System.out.println("Adding Sugar and Milk");
}
void prepareRecipe(){
boilWater();
brewCoffeeGrinds();
pourInCup();
addSugarAndMilk();
}
}
Tea.java
package TemplatePattern.first;
public class Tea {
public void boilWater(){
System.out.println("Boiling water");
}
public void steepTeaBag(){
System.out.println("Steeping the tea");
}
public void pourInCup(){
System.out.println("Pouring into cup");
}
public void addLemon(){
System.out.println("Adding Lemon");
}
void prepareRecipe(){
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}
}
从类图我们可以看出,这两个类的大部分方法是相同的:
员工A根据自己的所学,说到,我们可以将他们相同的部分和变化的部分分离开来:
这样一来,重复的代码就提取到父类里去了。 本次任务圆满结束:
CaffeineBeverage.java
package TemplatePattern.first;
/**
* 将Coffee,Tea中的方法抽象成模板类
* 模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现
*/
public abstract class CaffeineBeverage {
public final void prepareRecipe(){
boilWater();
brew();
pourInCup();
addCondiments();
}
protected abstract void brew();
protected abstract void addCondiments();
void boilWater(){
System.out.println("Boiling water");
}
void pourInCup(){
System.out.println("Pouring into cup");
}
}
NewCoffee.java
package TemplatePattern.first;
public class NewCoffee extends CaffeineBeverage {
@Override
protected void brew() {
System.out.println("Dripping Coffee through filter");
}
@Override
protected void addCondiments() {
System.out.println("Adding Sugar and Milk");
}
}
NewTea.java
package TemplatePattern.first;
public class NewTea extends CaffeineBeverage {
@Override
protected void brew() {
System.out.println("Steeping the tea");
}
@Override
protected void addCondiments() {
System.out.println("Adding Lemon");
}
}
其中CaffeineABeverage中的prepareRecipe方法我们称之为模板方法。请结合代码仔细品下下面的话:
模板方法模式使得子类可以在不改变算法结构的情况下重新定义算法的某些步骤。
2.2 对模板方法进行可控操作
客户有来问题了,现在他们想要CaffeineBeverage中的这个方法的一部分根据某些条件来执行,而不是无脑执行:
public final void prepareRecipe(){
boilWater();
brew();
pourInCup();
addCondiments();
}
这时,我们又该怎么做呢? 你是否听说过钩子? 在程序设计上,钩子的作用就是让用户控制某些方法是否执行。
例如:
package TemplatePattern.base;
/**
* 使用钩子
*/
public abstract class CaffeineBeverageWithHook {
public void prepareRecipe(){
boilWater();
brew();
pourInCup();
//这里使用钩子方法进行判断上是否执行添加调料方法
if(customerWantsCondiments()){
addCondiments();
}
}
protected abstract void brew();
protected abstract void addCondiments();
void boilWater(){
System.out.println("Boiling water");
}
void pourInCup(){
System.out.println("Pouring into cup");
}
//钩子方法,用来返回客户是否想要调料,默认返回true
public boolean customerWantsCondiments(){
return true;
}
}
上面的类里的customerWantsCondiments方法就是钩子方法,他被使用在模板方法中用来控制addCondiments方法是否执行。
我们可以在基类里对每个方法的执行都加一个钩子,然后让子类继承它并选择覆盖这些钩子方法达到控制方法的执行。
ULM如下:
代码路径如下:
设计模式/src/main/java/TemplatePattern/second · 严家豆/设计模式 - 码云 - 开源中国 (gitee.com)
关于钩子的用法,除此之外,我们还可以将它作为增强类方法能力的接口,类似与代理,例如,我们如果想在prepareRecipe执行过程中想让boilWater方法之前执行某些动作我们可以对boilWaterHook进行重写,并返回是否让boilWater执行的标识。总之钩子方法是个很好的东西,我们要善于控制它。
好啦,让我们再根据上面的例子细细品味下这个模式:
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。 模板方法模式使得子类可以在不改变算法结构的情况下重新定义算法的某些步骤。
模板方法模式的应用非常容易,也是一个比较聪明的设计模式,它可以将复杂的,公共的调用流程以及一些公共复杂的实现提前被写好,使用者可以根据自己的情况来选择性覆盖,这能大大节省开发者的时间。