模板方法模式定义:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的条件下,重新定义算法中的某些步骤
准备一杯咖啡:加咖啡粉 - 加热水 - 冲泡 - 加糖
public class Coffee {
public void addCoffeePowder(){
System.out.println("加咖啡粉");
}
public void addHotWater(){
System.out.println("加热水");
}
public void brew(){
System.out.println("冲泡");
}
public void addSugar(){
System.out.println("加糖");
}
public void prepareCoffee(){
addCoffeePowder();
addHotWater();
brew();
addSugar();
}
}
准备一杯柠檬茶也差不多是同样的步骤:加茶叶 - 加热水 - 冲泡 - 加柠檬汁
public class LemonTea {
public void addTea(){
System.out.println("加茶叶");
}
public void addHotWater(){
System.out.println("加热水");
}
public void brew(){
System.out.println("冲泡");
}
public void addLemonJuice(){
System.out.println("加柠檬汁");
}
public void prepareLemonTea(){
addTea();
addHotWater();
brew();
addLemonJuice();
}
}
抽象出共同的父类,将相同的方法移动至父类中:
public class Beverage {
public void addHotWater(){
System.out.println("加热水");
}
public void brew(){
System.out.println("冲泡");
}
}
public class Coffee extends Beverage {
public void addCoffeePowder(){
System.out.println("加咖啡粉");
}
public void addSugar(){
System.out.println("加糖");
}
public void prepareCoffee(){
addCoffeePowder();
addHotWater();
brew();
addSugar();
}
}
public class LemonTea extends Beverage {
public void addTea(){
System.out.println("加茶叶");
}
public void addLemonJuice(){
System.out.println("加柠檬汁");
}
public void prepareLemonTea(){
addTea();
addHotWater();
brew();
addLemonJuice();
}
}
简洁多了,但是仔细观察,加咖啡与加茶叶可以抽象为加主料,加糖与加柠檬汁可以抽象为加配料方法:
public abstract class Beverage {
public abstract void addIngredient();
public void addHotWater(){
System.out.println("加热水");
}
public void brew(){
System.out.println("冲泡");
}
public abstract void addCondiment();
}
public class Coffee extends Beverage {
public void addIngredient(){
System.out.println("加咖啡粉");
}
public void addCondiment(){
System.out.println("加糖");
}
public void prepareCoffee(){
addIngredient();
addHotWater();
brew();
addCondiment();
}
}
public class LemonTea extends Beverage {
public void addIngredient(){
System.out.println("加茶叶");
}
public void addCondiment(){
System.out.println("加柠檬汁");
}
public void prepareLemonTea(){
addIngredient();
addHotWater();
brew();
addCondiment();
}
}
此时已经可以清楚的发现,prepareCoffee与prepareLemonTea方法所包含的步骤是完全一样的,可以移动至共同父类中
public abstract class Beverage {
public abstract void addIngredient();
public void addHotWater(){
System.out.println("加热水");
}
public void brew(){
System.out.println("冲泡");
}
public abstract void addCondiment();
public void prepareBeverage(){
addIngredient();
addHotWater();
brew();
addCondiment();
}
}
public class Coffee extends Beverage {
public void addIngredient(){
System.out.println("加咖啡粉");
}
public void addCondiment(){
System.out.println("加糖");
}
}
public class LemonTea extends Beverage {
public void addIngredient(){
System.out.println("加茶叶");
}
public void addCondiment(){
System.out.println("加柠檬汁");
}
}
可以看到现在整个代码的结构已经完全符合模板方法模式的定义了
整体冲泡流程由父类的prepareBeverage方法控制,该方法为所有的子类提供统一的固定的准备流程,这里面固定的流程即为模板方法模式定义中的算法骨架
子类可以在不改变算法流程的条件下,重新定义算法中的某些步骤,如:冲泡不同饮品时需要加的主配料由特定饮品类实现
如果准备咖啡需要特定的流程,只需在Coffee类中重写Beverage的prepareBeverage方法即可
如果想要Beverage类所有子类的准备流程完全一致(严格按照加主料 - 加水 - 冲泡 - 加配料这一流程),且不允许修改,只需使用final修饰Beverage类的prepareBeverage方法即可
上面的设计对流程的控制太过严苛,很难在实际应用中使用,当然通过一些改动,使子类除了能够决定特定步骤的具体实现方式之外,还可以对流程进行控制
---- 2018-06-02
灵活,但是将使算法结构变得复杂混乱
如果需要大量这种改动,说明在当前业务场景下,模板方法模式并不是最好的选择,建造者模式可能是更好的选择
public abstract class Beverage {
public abstract void addIngredient();
public void addHotWater(){
System.out.println("加热水");
}
public void brew(){
System.out.println("冲泡");
}
public abstract void addCondiment();
protected boolean addOrNot(){
return true;
}
public void prepareBeverage(){
addIngredient();
addHotWater();
brew();
if(addOrNot())
addCondiment();
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Coffee extends Beverage {
public void addIngredient(){
System.out.println("加咖啡粉");
}
public void addCondiment(){
System.out.println("加糖");
}
protected boolean addOrNot(){
try {
System.out.println("Have some sugar?Y/N");
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String in = reader.readLine();
if(null == in || in.equals("") || in.equals("N")){
return false;
}
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
}
测试一下:
public class Test {
public static void main(String[] args) {
Beverage b = new Coffee();
b.prepareBeverage();
}
}
测试结果如下:
加咖啡粉
加热水
冲泡
Have some sugar?Y/N
Y
加糖
加咖啡粉
加热水
冲泡
Have some sugar?Y/N
N