咱們用泡茶和泡咖啡兩種行爲來引入這一設計模式。面試
思考一下「泡茶」的過程:算法
煮水 -> 用沸水泡茶葉 -> 把茶倒進杯子 -> 放點檸檬之類的佐料。設計模式
而後再看一下「泡咖啡」的過程:架構
煮水 -> 用沸水泡咖啡 -> 把咖啡倒進杯子 -> 加牛奶和糖。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"); } }
這個設計,必定意義上來講,達到了代碼重用的功能,可是違反了好萊塢原則,不能算是一個合格的設計。