一、 基本概述 下麵列出咖啡、茶的沖泡方法。 1.咖啡沖泡方法 (1) 把水煮沸 (2) 用沸水沖泡咖啡 (3) 把咖啡倒進杯子 (4) 家牛奶和糖 2.茶的沖泡方法 (1) 把水煮沸 (2) 用沸水浸泡茶葉 (3) 把茶倒進杯子 (4) 加檸檬 在使用代碼來完成這些方法時,我們一般想到的創建2個類 ...
一、 基本概述
下麵列出咖啡、茶的沖泡方法。
1.咖啡沖泡方法
(1) 把水煮沸
(2) 用沸水沖泡咖啡
(3) 把咖啡倒進杯子
(4) 家牛奶和糖
2.茶的沖泡方法
(1) 把水煮沸
(2) 用沸水浸泡茶葉
(3) 把茶倒進杯子
(4) 加檸檬
在使用代碼來完成這些方法時,我們一般想到的創建2個類(咖啡、茶類)來單獨實現這四個步驟。或者更好點是創建一個飲料父類來共用第一步與第三步,然後繼承父類實現各自飲料的第二步與第四步。那麼還有更好一點的方式來處理上面的問題嗎?有,可以使用模版方法模式。
二、詳細說明
1.模版方法模式:在一個方法中定義一個演算法的骨架,而將一些步驟延遲到子類中。模版方法使得子類可以在不改變演算法結構的情況下,重新定義演算法中的某些步驟。
這個模式是用來創建一個演算法的模版。什麼是模版?如你所見,模版就是一個方法。更具體地說,這個方法將演算法定義成一組步驟,其中的任何步驟都可以是抽象的,有子類負責實現。這可以確保演算法的結構保持不變,同時由子類提供部分實現。
鉤子是一種被聲明在抽象類中的方法,但只有空的或者預設的實現,鉤子的存在,可以讓子類有能力對演算法的不同點進行掛鉤。要不要掛鉤,由子類自行決定。
問:當我創建一個模版方法時,怎麼才能知道什麼時候該使用抽象方法,什麼時候使用鉤子呢?
答:當你的子類“必須”提供演算法中某個方法或步驟的實現時,就使用抽象方法。如果演算法的這個部分是可選的,就用鉤子。如果是鉤子的話,子類可以選擇實現這個鉤子,但並不強制這麼做。
問:使用鉤子真正的目的是什麼?
答:鉤子有幾種用法。鉤子可以讓子類實現演算法中可選的部分,或者在鉤子對於子類的實現並不重要的時候,子類可以對此鉤子置之不理。鉤子的另一個用法,是讓子類能夠有機會對模版方法中某些即將發生的步驟作出反應。鉤子也可以讓子類有能力為其抽象類作一些決定。
如在Asp.net MVC框架中,你創建一個控制器,預設都是繼承Controller類,而Controller類中就有鉤子,如OnActionExecuted、OnActionExecuting、OnResultExecuted、OnResultExecuting等,這些鉤子能夠在你的控制器中進行實現,以便控制或加工對請求的動作與返回的執行。
2.設計原則:好萊塢原則:別調用(打電話給)我們,我們會調用(打電話給)你。
好萊塢原則可以給我們一種防止“依賴腐敗”的方法。當高層組件依賴底層組件,而底層組件又依賴高層組件,而高層組件又依賴邊側組件,而邊側組件有依賴底層組件時,依賴腐敗就發生了。在這種情況下,沒有人可以輕易地搞懂系統是如何設計的。
在好萊塢原則之下,我們允許底層組件將自己掛鉤到系統上,但是高層組件會決定什麼時候和怎樣使用這些底層組件。換句話說,高層組件對待底層組件的方式是“別調用我們,我們會調用你”。
好萊塢原則和模版方法之間的連接其實還算明顯:當我們設計模版方法模式時,我們告訴子類,“不要調用我們,我們會調用你”。
問:底層組件不可以調用高層組件中的方法嗎?
答:並不盡然,事實上,底層組件在結束時,常常會調用從超類中繼承來的方法。我們所要做的是,避免讓高層和底層組件之間有明顯的環狀依賴。
模版方法模式是一個很常見的模式,到處都是。儘管如此,你必須擁有一雙銳利的眼鏡,因為模版方法有許多實現,而它們看起來並不一定和書上所說的設計一致。
這個模式很常見是因為對創建框架來說,這個模式好用。有框架控制如何做事情,而由你(使用這個框架的人)指定框架演算法中每個步驟的細節。
如在C#數組中,有Array類提供一個Sort()方法用來排序,該方法有2個參數(一個是數組,一個是IComparer 介面),Sort方法提供了排序演算法,實現IComparer 介面的參數提供了數組元素怎麼進行比較大小。
問:這真的是一個模版方法模式嗎?
答:我們都知道,荒野中的模式並非總是如同教科書例子一般地中規中矩,為了符合當前的環境和實現的約束,它們總是要被適當地修改。這個Array類的Sort()方法的設計者受到一些約束,通常我們無法設計一個類繼承C#數組,而Sort()方法希望能夠適用於所有的數組(每個數組都是不同的類)。所以它們定義了一個靜態方法,而由被排序的對象內的每個元素自行提供比較大小的演算法部分。所以,這雖然不是教科書上的模版方法,但它的實現仍然符合模版方法模式的精神。再者,由於不需要繼承數組可以使用這個演算法,這樣使得排序變得更有彈性、更有用。
4.總結:
1.好萊塢原則告訴我們,將決策權放在高層模塊中,以便決定如何以及何時調用底層模塊。
2.你將咋真實世界代碼中看到模版方法模式的許多變體,不要期待它們全都是一眼就可以被你認出的。
3.策略模式和模版方法模式都封裝演算法,一個用組合,一個用繼承。
4.工廠方法是模版方法的一種特殊版本。
三、代碼列表
public abstract class CaffeineBeverage { public void PrepareRecipe() { BoilWater(); Brew(); PourInCup(); AddCondiments(); Hook(); } protected abstract void Brew(); protected abstract void AddCondiments(); private void BoilWater() { Console.WriteLine("Boiling water"); } private void PourInCup() { Console.WriteLine("Pouring into cup"); } protected virtual void Hook() { } } public class Coffee:CaffeineBeverage { protected override void Brew() { Console.WriteLine("沖泡咖啡"); } protected override void AddCondiments() { } } public class Tea : CaffeineBeverage { protected override void Brew() { Console.WriteLine("浸泡茶"); } protected override void AddCondiments() { Console.WriteLine("加檸檬"); } }View Code
---------以上內容根據《Head First Design Mode》進行整理