準備一個(gè)抽象類(lèi),將部分邏輯以具體方法以及具體構造子的形式實(shí)現,然后聲明一些抽象方法來(lái)迫使子類(lèi)實(shí)現剩余的邏輯。不同的子類(lèi)可以以不同的方式實(shí)現這些抽象方法,從而對剩余的邏輯有不同的實(shí)現。這就是模版方法模式的用意。
很多人可能沒(méi)有想到,模版方法模式實(shí)際上是所有模式中最為常見(jiàn)的幾個(gè)模式之一,而且很多人可能使用過(guò)模版方法模式而沒(méi)有意識到自己已經(jīng)使用了這個(gè)模式。模版方法模式是基于繼承的代碼復用的基本技術(shù),模版方法模式的結構和用法也是面向對象設計的核心。
模版方法模式需要開(kāi)發(fā)抽象類(lèi)和具體子類(lèi)的設計師之間的協(xié)作。一個(gè)設計師負責給出一個(gè)算法的輪廓和骨架,另一些設計師則負責給出這個(gè)算法的各個(gè)邏輯步驟。代表這些具體邏輯步驟的方法稱(chēng)做基本方法(primitive method);而將這些基本法方法總匯起來(lái)的方法叫做模版方法(templatemethod),這個(gè)設計模式的名字就是從此而來(lái)。
模版方法模式的靜態(tài)結構如下圖所示。

這里涉及到兩個(gè)角色:
定義了一個(gè)或多個(gè)抽象操作,以便讓子類(lèi)實(shí)現。這些抽象操作叫做基本操作,它們是一個(gè)頂級邏輯的組成步驟。
定義并實(shí)現了一個(gè)模版方法。這個(gè)模版方法一般是一個(gè)具體方法,它給出了一個(gè)頂級邏輯的骨架,而邏輯的組成步驟在相應的抽象操作中,推遲到子類(lèi)實(shí)現。頂級邏輯也有可能調用一些具體方法。
實(shí)現父類(lèi)所定義的一個(gè)或多個(gè)抽象方法,它們是一個(gè)頂級邏輯的組成步驟。
每一個(gè)抽象模版角色都可以有任意多個(gè)具體模版角色與之對應,而每一個(gè)具體模版角色都可以給出這些抽象方法(也就是頂級邏輯的組成步驟)的不同實(shí)現,從而使得頂級邏輯的實(shí)現各不相同。






















































使用繼承作為復用的手段必須慎重,C#語(yǔ)言的設計師對使用繼承作為復用的工具有著(zhù)不同層次上的認識。
不知其一
首先,初學(xué)C#的程序員可能不知道什么是繼承,或者認為"繼承"是高深的工具。那時(shí)候,大部分的功能復用都是通過(guò)委派進(jìn)行的。
知其一、不知其二
然后慢慢地,他們發(fā)現在C#語(yǔ)言里實(shí)現繼承并不困難,并且初步認識到繼承可以使子類(lèi)一下子得到基類(lèi)的行為。這時(shí)他們就會(huì )躍躍欲試了,試圖使用繼承作為功能復用的主要工具,并把原來(lái)應當使用委派的地方,改為使用繼承,這時(shí)繼承就有被濫用的危險。
知其二
很多面向對象的設計專(zhuān)家從1986年就開(kāi)始警告繼承關(guān)系被濫用的可能。有一些面向對象的編程語(yǔ)言,如SELF語(yǔ)言,甚至將類(lèi)的繼承關(guān)系從語(yǔ)言的功能中取消掉,改為完全使用委派。
其他的設計師雖然不提倡徹底取消繼承,但無(wú)一例外地鼓勵在設計中盡可能使甩委派關(guān)系代替繼承關(guān)系。比如在【GOF95】一書(shū)中,狀態(tài)模式、策略模式、裝飾模式、橋梁模式以及抽象工廠(chǎng)模式均是將依賴(lài)于繼承的實(shí)現轉換為基于對象的組合和聚合的實(shí)現,這些模式的要點(diǎn)就是使用委派關(guān)系代替繼承關(guān)系。
知其三
是不是繼承就根本不該使用呢?事實(shí)上對數據的抽象化、繼承、封裝和多態(tài)性并稱(chēng)C#和其他絕大多數的面向對象語(yǔ)言的幾項最重要的特性。繼承不應當被濫用,并不意味著(zhù)繼承根本就不該使用。因為繼承容易被濫用就徹底拋棄繼承,無(wú)異于因噎廢食。
繼承使得類(lèi)型的等級結構易于理解、維護和擴展,而類(lèi)型的等級結構非常適合于抽象化的設計、實(shí)現和復用。盡管【GOF95】所給出的設計模式基本上沒(méi)有太多基于繼承的模式,很多模式都是用繼承的辦法定義、實(shí)現接口的。多數的設計模式都描寫(xiě)一個(gè)以抽象類(lèi)作為基類(lèi),以具體類(lèi)作為實(shí)現的等級結構,比如適配器模式、合成模式、橋梁模式、狀態(tài)模式等。
模版方法模式則更進(jìn)了一步:此模式鼓勵恰當地使用繼承。此模式可以用來(lái)改寫(xiě)一些擁有相同功能的相關(guān)的類(lèi),將可復用的一般性的行為代碼移到基類(lèi)里面,而把特殊化的行為代碼移到子類(lèi)里面。
因此,熟悉模版方法模式便成為一個(gè)重新學(xué)習繼承的好地方。
下面的例子演示了數據庫訪(fǎng)問(wèn)的模板方法。實(shí)際應用時(shí),請確保C盤(pán)根目錄下有nwind.mdb這個(gè)Access數據庫(可以從Office的安裝目錄下找到。中文版用戶(hù)的請注意字段名可能有所不同)。


















































































模版方法中的方法可以分為兩大類(lèi):模版方法(Template Method)和基本方法(Primitive Method)。
模版方法
一個(gè)模版方法是定義在抽象類(lèi)中的,把基本操作方法組合在一起形成一個(gè)總算法或一個(gè)總行為的方法。這個(gè)模版方法一般會(huì )在抽象類(lèi)中定義,并由子類(lèi)不加以修改地完全繼承下來(lái)。
基本方法
基本方法又可以分為三種:抽象方法(Abstract Method)、具體方法(Concrete Method)和鉤子方法(Hook Method)。
抽象方法:一個(gè)抽象方法由抽象類(lèi)聲明,由具體子類(lèi)實(shí)現。在C#語(yǔ)言里一個(gè)抽象方法以abstract關(guān)鍵字標示出來(lái)。
具體方法:一個(gè)具體方法由抽象類(lèi)聲明并實(shí)現,而子類(lèi)并不實(shí)現或置換。在C#語(yǔ)言里面,一個(gè)具體方法沒(méi)有abstract關(guān)鍵字。
鉤子方法:一個(gè)鉤子方法由抽象類(lèi)聲明并實(shí)現,而子類(lèi)會(huì )加以擴展。通常抽象類(lèi)給出的實(shí)現是一個(gè)空實(shí)現,作為方法的默認實(shí)現。(VisualFoxPro中項目向導建立的項目會(huì )使用一個(gè)AppHook類(lèi)實(shí)現監視項目成員變化,調整系統結構的工作。)鉤子方法的名字通常以do開(kāi)始。
在對一個(gè)繼承的等級結構做重構時(shí),一個(gè)應當遵從的原則便是將行為盡量移動(dòng)到結構的高端,而將狀態(tài)盡量移動(dòng)到結構的低端。
1995年,Auer曾在文獻【AUER95】中指出:
如果能夠遵從這樣的原則,那么就可以在等級結構中將接口與實(shí)現分隔開(kāi)來(lái),將抽象與具體分割開(kāi)來(lái),從而保證代碼可以最大限度地被復用。這個(gè)過(guò)程實(shí)際上是將設計師引導到模版方法模式上去。
聯(lián)系客服