模板方法模式:在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類能夠在不改變算法結構的狀況下,從新定義算法中的某些步驟。算法
從定義中,應該能夠看出一部分,爲了更好理解,下面就直接上例子:設計模式
在敲代碼時,累了喝杯咖啡或者喝杯茶,會精神倍增。其實不管咖啡仍是茶在衝的時間都是有講究的。這個在本文不是重點。下面分別描述一下衝泡咖啡和沖泡茶的過程:ide
兩種茶其分別的作法以下代碼:測試
1 public class Coffee 2 { 3 public void PrepareRecipe() 4 { 5 //燒水 6 BoilWater(); 7 //衝咖啡 8 BrewCoffeeGrinds(); 9 //倒入茶杯中 10 PourInCup(); 11 //加入糖和咖啡 12 AddSugarAndMilk(); 13 } 14 public void BoilWater() 15 { 16 Console.WriteLine("燒水"); 17 } 18 19 public void BrewCoffeeGrinds() 20 { 21 Console.WriteLine("衝咖啡"); 22 } 23 24 public void PourInCup() 25 { 26 Console.WriteLine("倒入杯子中"); 27 } 28 29 public void AddSugarAndMilk() 30 { 31 Console.WriteLine("加糖和牛奶"); 32 } 33 } 34 35 public class Tea 36 { 37 public void PrepareRecipe() 38 { 39 //燒水 40 BoilWater(); 41 //泡茶 42 SteepTeaBag(); 43 //倒入茶杯中 44 PourInCup(); 45 //加入檸檬 46 AddLemon(); 47 } 48 public void BoilWater() 49 { 50 Console.WriteLine("燒水"); 51 } 52 53 public void SteepTeaBag() 54 { 55 Console.WriteLine("泡茶"); 56 } 57 58 public void PourInCup() 59 { 60 Console.WriteLine("倒入杯子中"); 61 } 62 63 public void AddLemon() 64 { 65 Console.WriteLine("加檸檬"); 66 } 67 }
在面嚮對象語言中,是要講求複用的,如今燒水和帶入杯子的方法顯然是重複的,這樣就不符合對象村的村規——複用。
對比兩種作法,都是須要四個步驟,能不能把相同的使用一個基類,不一樣的部分分別由本身的去實現。其類圖以下編碼
若是再細緻觀察的話,咱們的衝咖啡和泡茶以及加入咖啡和牛奶都是屬於差很少動做相同的。因此能夠繼續抽象,抽象後的方法大體如此:spa
其實得出的也就是咱們的模板方法。下面來看看模板方法模式的類圖:設計
3、模板方法類圖3d
從類圖能夠看到咱們把共用的方法放在抽象類中,用於複用。把不肯定的方法,放入到具體類中,以便讓具體類能夠很好的構造本身的方法。除此以外,還有個重點是不管是構造好的方法也好,仍是抽象的方法也好,都會被裝入到一個TemplateMethod方法中。就像咱們平時的作項目也是如此,大體的步驟須要:需求分析——>編碼——>測試,這些最基本的過程。可是具體的每一步,可能都是不一樣的,可是作項目的過程的幾個步驟是基本不變的,把步驟抽象成了模板,之後作項目的時間,都按這個步驟去作。這個就是咱們的模板方法模式。到這裏仍是把咱們的茶和咖啡沏好。code
基類代碼:對象
public abstract class CoffeeinBeverage1 { public void BoilWater() { Console.WriteLine("燒水"); } public void PrepareRecipe() { BoilWater(); Brew(); PourInCup(); AddCondiments(); } public void PourInCup() { Console.WriteLine("倒入杯子中"); } public abstract void Brew(); public abstract void AddCondiments(); }
咖啡代碼:
public class Coffee1 : CoffeeinBeverage1 { public override void Brew() { Console.WriteLine("衝咖啡"); } public override void AddCondiments() { Console.WriteLine("加糖和牛奶"); }
茶代碼:
public class Tea1 : CoffeeinBeverage1 { public override void Brew() { Console.WriteLine("泡茶"); } public override void AddCondiments() { Console.WriteLine("加檸檬"); } }
在上面的茶水和咖啡中,如今是看起來能喝了,可是有個問題就是有些人喝咖啡喜歡不加任何調味料的。那麼咱們硬是給客人加,確定是會生氣的。爲了知足這個要求,設計模式提供的有這個解決方案——使用鉤子。具體什麼是鉤子,咱們小的時間的課本上有個猴子撈月亮的圖片:
每一個猴子不只會被其餘猴子拉住,還會伸出一個手去拉其餘猴子。但最後一個手是沒有拉其餘猴子了,可是仍是要伸下去的,以防碰不到月亮。
一樣不知道方法能不能用獲得,可是有對應的方法,具體的什麼時間用獲得,什麼時間用不到,根據條件來判斷。
下面來看看鉤子如何在模板方法模式中使用的:
基類代碼:
public void PrepareRecipe() { BoilWater(); Brew(); PourInCup(); //有個判斷方法來添加調料 if (WantCondiments()) { AddCondiments(); } } /// <summary> /// 加入一個方法,用來判斷是否須要加調料 /// </summary> /// <returns></returns> public virtual bool WantCondiments() { return true; }
在子類中,能夠經過不一樣的方式類覆蓋WantCondiment()方法。用來表示是否要加調料的標準,在此方法中注意必須加入virtual關鍵字,以便子類中使用override重寫。下面看看咖啡中的方法:
public override bool WantCondiments() { return false; }
下面是測試代碼:
class Program { static void Main(string[] args) { CoffeeinBeverage1 coffeninBeverage = new Coffee1(); coffeninBeverage.PrepareRecipe(); Console.ReadKey(); } }
輸出結果:
發現已經去掉了調料。
在看模板方法的時間,很容易想到工廠方法。由於他們都是讓具體的實現放在子類中,可是工廠方法主要是生產出產品,而後去應用產品。模板方法是在於依賴子類中的步驟中的其中幾個步驟,具體的步驟已經在基類中寫好了。
一樣模板方法模式和策略模式都是封裝算法。可是策略模式中的每一個策略都是單獨的一個類。能夠隨時去更改策略。模板方法模式雖然也是封裝了算法,其實主要在於封裝步驟,具體的實現是根據依靠各個子類。
除此以外,模板方法模式還涉及到一個好萊塢原則:
不要給我打電話,我會主動和你打電話。
在模板方法模式中扮演好萊塢的角色是抽象類,子類是演員的角色。
通常須要調用子類中的方法都已經在模板中定義好了,須要時,會主動聯繫各個步驟,最好在子類中不要去調用抽象類中的方法具體方法,以防止子類和父類的調用的凌亂。
本文主要先列出了模板方法模式的定義,而後經過例子來幫助理解模式,接着提出鉤子在模板方法模式中的使用,最後簡單對比了模板方法模式、策略模式以及工廠方法模式。