設計模式(二十二)模板方法模式 Template

  • 泡茶?泡咖啡?

  咱們用泡茶和泡咖啡兩種行爲來引入這一設計模式。面試

  思考一下「泡茶」的過程:算法

  煮水 -> 用沸水泡茶葉 -> 把茶倒進杯子 -> 放點檸檬之類的佐料。設計模式

  而後再看一下「泡咖啡」的過程:架構

  煮水 -> 用沸水泡咖啡 -> 把咖啡倒進杯子 -> 加牛奶和糖。ide

 

  若是咱們用兩個類去描述這兩個過程,很明顯會有不少重複的代碼(例如 Step1 煮水,Step3 倒進杯子),也有不少類似的代碼(Step2 沖泡,Step4 加佐料)。spa

  將沖泡的過程看作是一個算法,那麼這個算法的主架構是一致的:翻譯

  煮水 -> 用沸水泡東西 -> 將泡完的東西倒進杯子 -> 加佐料。設計

  將主過程抽象出來,至於泡什麼東西,加什麼佐料,就交給子類去實現,這就是模板方法模式。code

 

  • 模板方法模式:

  在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類能夠在不改變算法結構的狀況下,從新定義算法中的某些步驟。blog

 

  • UML:

 

  除了上述的四個步驟,這裏加了個新的方法:customerWantsCondiments(),由於加佐料這個步驟不是必須的。

 

  • 模板方法的組成:

  從上面 UML 圖,咱們可以發現,在抽象類 BeverageDrive 中,一共存在4類方法:

  1. 模板方法:prepareRecipe(),這是算法的主體,在方法內部會調用其餘的方法,通常來講是 public final 的。
  2. 抽象方法:brew() & addCondiments(),是算法步驟的「個性」,在 BeverageDrive 中聲明爲 protected abstract,由子類去實現。
  3. 具體方法:boilWater() & pourInCup(),是算法步驟的「共性」,通常是 private 的。
  4. 鉤子方法:customerWantsCondiments,具備空的或是默認的實現。鉤子的存在,可讓子類有能力對算法的不一樣點進行掛鉤,選擇權在子類手上,通常是 protected 的。

 

  • 代碼:
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");
    }
}

 

  這個設計,必定意義上來講,達到了代碼重用的功能,可是違反了好萊塢原則,不能算是一個合格的設計。

相關文章
相關標籤/搜索