1. 程式人生 > >模板方法模式( Template Method Pattern ): 工廠方法模式的情侶

模板方法模式( Template Method Pattern ): 工廠方法模式的情侶

  1. 參考書籍: 《Design Patterns: Elements of Reusable Object-Oriented Software》

模板方法模式和工廠方法模式分別隸屬於行為模式(Behavioral Pattern)和建立類模式(建立型模式), 但是他們的核心思想卻十分相似, 而且通常會同時出現。

模板方法模式

  • 設計動機
    • 將一個操作整體步驟定義好,把其中的一部分具體步驟延遲到子類去實現。模板方法可以讓子類在不改變演算法整體結構的情況下, 重新定義其中的特定步驟。
    • 舉例: 假設現在要編寫一個支援屠宰家畜的框架, 家畜的型別包括牛羊等, 於是希望先定義一個屠宰場類 KillHouse, 該屠宰場會在呼叫KillAnimal(Animal animal) 方法時會接受執行時傳入的引數(Sheep、Chicken ) 。現在假設, 宰殺家畜這個行為的整體操作流程是基本固定的,比如都是首先要判斷傳入的物件是不是可宰殺的牲畜, 如果可宰殺, 則先做一些準備工作customPrepareWork(),需要的準備工作可能會因為牲畜型別不同而不同, 然後呼叫一把刀(Knife)的chop( animal)方法, 宰殺完畢以後, 事後再進行一些通用的清理現場的工作 。
      • 在以上的描述中 , 假設了宰殺牲畜前的準備工作因動物而異, 宰殺後的清理工作對於不同型別的牲畜都是通用的, 所以這一部分是可以被複用的程式碼, 可以放到父類中實現。 然後由子類去重寫準備工作的方法customPrepareWork()。
abstract public class KillHouse {

    private Knife knife = new DefaultKnife();

    public void killAnimal(Animal animal)
    {
        if(animal.canBeKilled)
        {
            // 有一些因動物類別而不同的準備步驟需要
            customPrepareWork();
            knife.chop(animal); // 宰殺動物的操作
            // 有一些通用的清理步驟
commonCleanWork(); } else{ // 報警: 遭遇了不可宰殺的動物 } } protected abstract void customPrepareWork(); private void commonCleanWork() { //通用的清理步驟 } }
public class KillCowHouse extends KillHouse {
    @Override
    protected void customPrepareWork
() { // 重寫父類的方法, 實現殺牛之前獨特的準備工作 } }

通過以上的程式碼可以發現, 父類固定了宰殺家禽killAnimal這項操作的整體結構或整體步驟, 實現了通用的清理工作, 子類KillCowHouse 重寫了需要根據動物型別定製的準備工作。 killAnimal( Animal animal) 這個方法就是一個模板方法

模板方法模式與工廠方法模式之間的情侶關係

注意到之前編寫的程式碼樣例中, 用到了Knife來宰殺動物, 這個物件的獲得是通過在父類裡直接new DefaultKnife 完成的, 這樣的話, 其實所有的動物,不論是牛還是雞都在用同一把刀宰殺, 現假設殺雞不能用牛刀的, 且想把刀的例項化工作,放到通用的準備工作中去, 則就會應用到工廠方法模式, 變成如下的樣子。

abstract public class KillHouse {

    private Knife knife = null; 

    public void killAnimal(Animal animal)
    {
        if(animal.canBeKilled)
        {
            // 有一些因動物類別而不同的準備步驟需要
            customPrepareWork();

            knife = createKnife();

            if(knife != null)
            {
                knife.chop(animal); // 宰殺動物的操作
                // 有一些通用的清理步驟
                commonCleanWork();
            }else{
                // 報警: 沒有成功獲取到刀。 
            }

        }
        else{
            // 報警: 遭遇了不可宰殺的動物
        }

    }

    protected abstract void customPrepareWork();
    protected abstract Knife createKnife();  // 新增的工廠方法

    private void commonCleanWork() {
        //通用的清理步驟
    }

}
public class KillCowHouse extends KillHouse {
    @Override
    protected Knife createKnife() {
        return new CowKnife();
    }

    @Override
    protected void customPrepareWork() {
        // 重寫父類的方法, 實現殺牛之前獨特的準備工作
    }


}

通過以上的例子可以發現, 由於子類可能需要使用的例項不同, 所以增加了一個工廠方法, 把刀的例項化操作留給了子類去重寫。 在這個過程中, 模板方法 killAnimal(Animal animal) 中呼叫了 createKnife() 這個工廠方法。

  • 總結:
    • 模板方式和工廠模式的核心思想非常類似, 都是把一些操作留給子類去實現。
    • 模板方法中常常會呼叫工廠方法的, 他們之間存在著的緊密的情侶關係。
    • 工廠方法模式和模板方法模式的區別在於:
      • 模板方法模式的意義在於固定了一個演算法的整體結構, 複用了其中通用的步驟, 將需要定製的部分留給了子類實現
      • 工廠方法模式的意義在於解決了父類沒有辦法預知應該實現什麼子類的問題