咱们用泡茶和泡咖啡两种行为来引入这一设计模式。面试
思考一下“泡茶”的过程:算法
煮水 -> 用沸水泡茶叶 -> 把茶倒进杯子 -> 放点柠檬之类的佐料。设计模式
而后再看一下“泡咖啡”的过程:架构
煮水 -> 用沸水泡咖啡 -> 把咖啡倒进杯子 -> 加牛奶和糖。ide
若是咱们用两个类去描述这两个过程,很明显会有不少重复的代码(例如 Step1 煮水,Step3 倒进杯子),也有不少类似的代码(Step2 冲泡,Step4 加佐料)。spa
将冲泡的过程看作是一个算法,那么这个算法的主架构是一致的:翻译
煮水 -> 用沸水泡东西 -> 将泡完的东西倒进杯子 -> 加佐料。设计
将主过程抽象出来,至于泡什么东西,加什么佐料,就交给子类去实现,这就是模板方法模式。code
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类能够在不改变算法结构的状况下,从新定义算法中的某些步骤。blog
除了上述的四个步骤,这里加了个新的方法:customerWantsCondiments(),由于加佐料这个步骤不是必须的。
从上面 UML 图,咱们可以发现,在抽象类 BeverageDrive 中,一共存在4类方法:
public abstract class BeverageDrive { // Template method public final void prepareRecipe() { boilWater(); brew(); pourInCup(); if (customerWantsCondiments()) { addCondiments(); } } // Abstract method protected abstract void brew(); protected abstract void addCondiments(); // Concrete method private void boilWater() { System.out.println("Boiling water"); } private void pourInCup() { System.out.println("Pouring into cup"); } // Hook method protected boolean customerWantsCondiments() { return true; } }
public final class CoffeeBeverage extends BeverageDrive { @Override public void brew() { System.out.println("Dripping coffee through filter"); } @Override public void addCondiments() { System.out.println("Adding sugar and milk"); } @Override public boolean customerWantsCondiments() { return false; } }
public final class TeaBeverage extends BeverageDrive { @Override public void brew() { System.out.println("Steeping the tea"); } @Override public void addCondiments() { System.out.println("Adding lemon"); } }
好莱坞在寻找演员的时候有一个著名的原则:别打电话给咱们,咱们会打电话给你。
这一点在 OO 设计上,被翻译为:别调用我,我会调用你。
在好莱坞,演员属于低层组件,电影公司属于高层组件。每当电影公司须要演员的时候,都是电影公司打电话通知演员来面试。
换句话说:高层组件对待低层组件的方式是:别调用我,我会调用你。
当咱们设计模板方法模式的时候,要时刻记得,让父类去调用子类,不要让子类调用父类方法。
下面看一个不太好的设计:
public abstract class BadBehaviorBeverageDrive { protected void boilWater() { System.out.println("Boiling water"); } protected void pourInCup() { System.out.println("Pouring into cup"); } protected boolean customerWantsCondiments() { return true; } }
public final class BadBehaviorTeaBeverage extends BadBehaviorBeverageDrive { public final void prepareRecipe() { super.boilWater(); steepTeaBags(); super.pourInCup(); if (super.customerWantsCondiments()) { addLemon(); } } private void steepTeaBags() { System.out.println("Steeping the tea"); } private void addLemon() { System.out.println("Adding lemon"); } }
这个设计,必定意义上来讲,达到了代码重用的功能,可是违反了好莱坞原则,不能算是一个合格的设计。