1. 程式人生 > >設計模式3.Template Method(模板方法)

設計模式3.Template Method(模板方法)

設計模式目的:

模板方法,在父類中定義好演算法步驟順序,將演算法中的某一具體部分延遲到子類中實現,使得可以在不改變演算法的前提下,將體重特定的部分改變實現。

XML類圖:

模式核心概述:

從模式描述中就能知道,模板方法模式的類圖很簡單,僅涉及到父類和子類。

1.父類實現一個模版方法,定義具體的演算法;

2.父類提供演算法中可變部分的介面,供子類實現;

3.子類實現可變部分的介面

程式碼實現:

1.實現父類演算法,提供抽象介面

namespace TemplateMethod
{
    public class Weekend
    {
        public void Plain()
        {
            GetUp();
            Sleep();
            DoSomething();
        } 

        public virtual void GetUp()
        {

        }

        public virtual void Sleep()
        {

        }

        public virtual void DoSomething()
        {

        }
    }
}

Plain就是一個模板方法,定義了週末一天的計劃。但是每個步驟具體要怎麼做,又沒有完全限定死,而是做成了抽象介面供子類自由實現。

2.子類具體實現

實現一個悠閒的週末類:

namespace TemplateMethod
{
    public class LeisureWeekend : Weekend
    {
        public override void GetUp()
        {
            Console.WriteLine("Get up at 11 am ");
        }

        public override void Sleep()
        {
            Console.WriteLine("Sleep at 10 pm");
        }

        public override void DoSomething()
        {
            Console.WriteLine("Watch movie");
            Console.WriteLine("Play Game");
            Console.WriteLine("Riding");
        }


    }
}

再實現一個忙碌的週末類:

namespace TemplateMethod
{
    public class BusynessWeekend : Weekend
    {
        public override void GetUp()
        {
            Console.WriteLine("Get up at 9 am ");
        }

        public override void Sleep()
        {
            Console.WriteLine("Sleep at 12 pm");
        }

        public override void DoSomething()
        {
            Console.WriteLine("Work...");
            Console.WriteLine("Work...");
            Console.WriteLine("Work...");
        }
    }
}

3.呼叫程式碼:

namespace TemplateMethod
{
    class Program
    {
        static void Main(string[] args)
        {
            Weekend leisure = new LeisureWeekend();
            leisure.Plain();

            Weekend busyness = new BusynessWeekend();
            busyness.Plain();

            Console.ReadKey();
        }
    }
}

從程式碼也可看出模板方法模式很簡單,用不了多少程式碼就能實現這個模式的例項。

其實,在實際工程中,絕大部分專案中或多或少都用到了該模式。因為這種模式實在是太基礎,太易於被使用了。

舉個簡單例子,比如在卡牌遊戲中,有冒險、金幣、常規關卡、爬塔等幾個模式,從點選主介面按鈕到真正進入遊戲,雖然流程基本一致,但每個模式開啟介面讀表、獎勵提示等資料來源不一致,這時候就可以使用模板方法。

ClickButton();                               // 點選某個模式按鈕

OpenRewardsWindow();              // 開啟不同模式的獎勵介面,讀取不同的獎勵表

PreEnterProcess();                       // 進入副本前的操作,比如扣體力、載入一些配置表

EnterMap();                                  // 開始進入地圖

...

實現一個虛擬函式就是模板方法了嗎?!

從某種意義上可以這樣說,父類的一個虛擬函式本來從廣義上來講就是一個演算法,這樣只要有子類去實現了這個介面就可以算得上是個模板方法。但如果真這樣,模板方法也就太廉價了。

在GOF的《設計模式》一書中,講模板方法歸類為行為類模式。所謂行為類,蘊含了兩方面含義。

1.行為--意指這種模式重點關注的是行為

2.類--這種行為是在類中間傳遞的

行為如何在類中傳遞,只能是繼承。模板方法可變部分就是在子類和父類中傳遞的行為。

初學者可能會將關注點放在虛擬函式的實現這個點上,然而就如名字一樣,模板方法才是這個設計模式的真正核心價值所在。將一個演算法歸納,不變的部分放入父類,將可變的部門抽象出來,用虛擬函式實現供子類複寫。如何去設計好這個演算法,去剝離可變、不可變兩部分才是重點分析設計所在。如何設計好這個模板方法,關係到這個模式的穩定性。

如果僅僅是一個虛擬函式,可以說只包含了可變實現,並沒有通用不變的部分,這樣還算是一個合理的演算法嗎?

所以,設計好演算法,尤其是某個演算法整體流程非常固定,幾乎不會改變,只是某部門實現比較多變靈活,不妨考慮下模版方法。

最後,用一個關聯模式,回溯下之前的內容同時,也再將模板設計模式深刻入腦中。

設計模式1 Iterator模式中,我們有提到Foreach內部做了些操作,它需要一個IEnumerator物件,並且呼叫IEnumerator物件的Current屬性和MoveNext介面。看著是不是很眼熟呢?Foreach是不是就有點像一個模版方法?!只是說這個模版方法被C#語言給隱藏起來了,但它的演算法是固定了的。

我們可以大致模擬一下foreach的程式碼:

// foreach
IEnumerator enumerator = XXXX.GetEnumerator();
while(enumerator.MoveNext()){
    var value = enumerator.Current;
}

這就是一個類似的模版方法,演算法是固定的,MoveNext和Current是抽象可變的。不過略有區別的是,這個模版方法是屬於類的外部方法,不是在IEnumerator類的內部。演算法和抽象在不同類中實現,所以IEnumerator不能算作是實現了模版設計模式的一個類。