設計模式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不能算作是實現了模版設計模式的一個類。