模板方法模式 :Template Method(轉自阿良.NET)

來源:互聯網
上載者:User
準備一個抽象類別,將部分邏輯以具體方法以及具體構造子的形式實現,然後聲明一些抽象方法來迫使子類實現剩餘的邏輯。不同的子類可以以不同的方式實現這些抽象方法,從而對剩餘的邏輯有不同的實現。這就是模版方法模式的用意。

很多人可能沒有想到,模版方法模式實際上是所有模式中最為常見的幾個模式之一,而且很多人可能使用過模版方法模式而沒有意識到自己已經使用了這個模式。模版方法模式是基於繼承的代碼複用的基本技術,模版方法模式的結構和用法也是物件導向設計的核心。

模版方法模式需要開發抽象類別和具體子類的設計師之間的協作。一個設計師負責給出一個演算法的輪廓和骨架,另一些設計師則負責給出這個演算法的各個邏輯步驟。代表這些具體邏輯步驟的方法稱做基本方法(primitive method);而將這些基本法方法總匯起來的方法叫做模版方法(template method),這個設計模式的名字就是從此而來。

二、 模版方法模式的結構

模版方法模式的靜態結構如所示。

這裡涉及到兩個角色:

抽象模版(AbstractClass)角色有如下的責任:

定義了一個或多個抽象操作,以便讓子類實現。這些抽象操作叫做基本操作,它們是一個頂級邏輯的組成步驟。

定義並實現了一個模版方法。這個模版方法一般是一個具體方法,它給出了一個頂級邏輯的骨架,而邏輯的組成步驟在相應的抽象操作中,延遲到子類實現。頂級邏輯也有可能調用一些具體方法。

具體模版(ConcreteClass)角色有如下的責任:

實現父類所定義的一個或多個抽象方法,它們是一個頂級邏輯的組成步驟。

每一個抽象模版角色都可以有任意多個具體模版角色與之對應,而每一個具體模版角色都可以給出這些抽象方法(也就是頂級邏輯的組成步驟)的不同實現,從而使得頂級邏輯的實現各不相同。

三、 模板方法模式的示意性代碼

複製  儲存
// Template Method pattern -- Structural example  using System;// "AbstractClass"abstract class AbstractClass{  // Methods  abstract public void PrimitiveOperation1();  abstract public void PrimitiveOperation2();  // The Template method  public void TemplateMethod()  {    Console.WriteLine("In AbstractClass.TemplateMethod()");    PrimitiveOperation1();    PrimitiveOperation2();  }}// "ConcreteClass"class ConcreteClass : AbstractClass{  // Methods  public override void PrimitiveOperation1()  {    Console.WriteLine("Called ConcreteClass.PrimitiveOperation1()");  }  public override void PrimitiveOperation2()  {    Console.WriteLine("Called ConcreteClass.PrimitiveOperation2()");  }}/**//// <summary>/// Client test/// </summary>public class Client{  public static void Main( string[] args )  {    // Create instance and call template method    ConcreteClass c = new ConcreteClass();    c.TemplateMethod();  }}

四、 繼承作為複用的工具

使用繼承作為複用的手段必須謹慎,C#語言的設計師對使用繼承作為複用的工具有著不同層次上的認識。

不知其一

首先,初學C#的程式員可能不知道什麼是繼承,或者認為"繼承"是高深的工具。那時候,大部分的功能複用都是通過委派進行的。

知其一、不知其二

然後慢慢地,他們發現在C#語言裡實現繼承並不困難,並且初步認識到繼承可以使子類一下子得到基類的行為。這時他們就會躍躍欲試了,試圖使用繼承作為功能複用的主要工具,並把原來應當使用委派的地方,改為使用繼承,這時繼承就有被濫用的危險。

知其二

很多物件導向的設計專家從1986年就開始警告繼承關係被濫用的可能。有一些物件導向的程式設計語言,如SELF語言,甚至將類的繼承關係從語言的功能中取消掉,改為完全使用委派。

其他的設計師雖然不提倡徹底取消繼承,但無一例外地鼓勵在設計中儘可能使甩委派關係代替繼承關係。比如在【GOF95】一書中,狀態模式、策略模式、裝飾模式、橋樑模式以及抽象原廠模式均是將依賴於繼承的實現轉換為基於對象的組合和彙總的實現,這些模式的要點就是使用委派關係代替繼承關係。

知其三

是不是繼承就根本不該使用呢?事實上對資料的抽象化、繼承、封裝和多態性並稱C#和其他絕大多數的物件導向語言的幾項最重要的特性。繼承不應當被濫用,並不意味著繼承根本就不該使用。因為繼承容易被濫用就徹底拋棄繼承,無異於因噎廢食。

繼承使得類型的等級結構易於理解、維護和擴充,而類型的等級結構非常適合於抽象化的設計、實現和複用。儘管【GOF95】所給出的設計模式基本上沒有太多基於繼承的模式,很多模式都是用繼承的辦法定義、實現介面的。多數的設計模式都描寫一個以抽象類別作為基類,以具體類作為實現的等級結構,比如適配器模式、合成模式、橋樑模式、狀態模式等。

模版方法模式則更進了一步:此模式鼓勵恰當地使用繼承。此模式可以用來改寫一些擁有相同功能的相關的類,將可複用的一般性的行為代碼移到基類裡面,而把特殊化的行為代碼移到子類裡面。

因此,熟悉模版方法模式便成為一個重新學習繼承的好地方。

五、 一個實際應用模板方法的例子

下面的例子示範了資料庫訪問的模板方法。實際應用時,請確保C盤根目錄下有nwind.mdb這個Access資料庫(可以從Office的安裝目錄下找到。中文版使用者的請注意欄位名可能有所不同)。

複製  儲存

// Template Method pattern -- Real World example  using System;using System.Data;using System.Data.OleDb;// "AbstractClass"abstract class DataObject{  // Methods  abstract public void Connect();  abstract public void Select();  abstract public void Process();  abstract public void Disconnect();  // The "Template Method"  public void Run()  {    Connect();    Select();    Process();    Disconnect();  }}// "ConcreteClass"class CustomerDataObject : DataObject{  private string connectionString =    "provider=Microsoft.JET.OLEDB.4.0; "    + "data source=c:\\nwind.mdb";  private string commandString;  private DataSet dataSet;   // Methods  public override void Connect( )  {    // Nothing to do  }  public override void Select( )  {    commandString = "select CompanyName from Customers";    OleDbDataAdapter dataAdapter = new OleDbDataAdapter(      commandString, connectionString );    dataSet = new DataSet();    dataAdapter.Fill( dataSet, "Customers" );  }  public override void Process()  {    DataTable dataTable = dataSet.Tables["Customers"];    foreach( DataRow dataRow in dataTable.Rows )      Console.WriteLine( dataRow[ "CompanyName" ] );  }  public override void Disconnect()  {    // Nothing to do  }}/**//// <summary>///  TemplateMethodApp test/// </summary>public class TemplateMethodApp{  public static void Main( string[] args )  {    CustomerDataObject c = new CustomerDataObject( );    c.Run();  }}

六、 模版方法模式中的方法

模版方法中的方法可以分為兩大類:模版方法(Template Method)和基本方法(Primitive Method)。

模版方法

一個模版方法是定義在抽象類別中的,把基本操作方法組合在一起形成一個總演算法或一個總行為的方法。這個模版方法一般會在抽象類別中定義,並由子類不加以修改地完全繼承下來。

基本方法

基本方法又可以分為三種:抽象方法(Abstract Method)、具體方法(Concrete Method)和鉤子方法(Hook Method)。

抽象方法:

一個抽象方法由抽象類別聲明,由具體子類實現。在C#語言裡一個抽象方法以abstract關鍵字標示出來。

具體方法:

一個具體方法由抽象類別聲明並實現,而子類並不實現或置換。在C#語言裡面,一個具體方法沒有abstract關鍵字。

鉤子方法:
一個鉤子方法由抽象類別聲明並實現,而子類會加以擴充。通常抽象類別給出的實現是一個空實現,作為方法的預設實現。(Visual FoxPro中項目嚮導建立的項目會使用一個AppHook類實現監視項目成員變化,調整系統結構的工作。)鉤子方法的名字通常以do開始。

七、 重構的原則

在對一個繼承的等級結構做重構時,一個應當遵從的原則便是將行為盡量移動到結構的高端,而將狀態盡量移動到結構的低端。

1995年,Auer曾在文獻【AUER95】中指出:

應當根據行為而不是狀態定義一個類。也就是說,一個類的實現首先建立在行為的基礎之上,而不是建立在狀態的基礎之上。 
在實現行為時,是用抽象狀態而不是用具體狀態。如果一個行為涉及到對象的狀態時,使用間接的引用而不是直接的引用。換言之,應當使用取值方法而不是直接引用屬性。 
給操作劃分層次。一個類的行為應當放到一個小組核心方法(Kernel Methods)裡面,這些方法可以很方便地在子類中加以置換。 
 將狀態屬性的確認延遲到子類中。不要在抽象類別中過早地聲明屬性變數,應將它們盡量地延遲到子類中去聲明。在抽象超類中,如果需要狀態屬性的話,可以調用抽象的取值方法,而將抽象的取值方法的實現放到具體子類中。
如果能夠遵從這樣的原則,那麼就可以在等級結構中將介面與實現分隔開來,將抽象與具體分割開來,從而保證代碼可以最大限度地被複用。這個過程實際上是將設計師引導到模版方法模式上去。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.