小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

模板方法模式(Template Method Pattern)

 漂在北方的狼 2007-03-28

一、 模板方法(Template Method)模式

準(zhǔn)備一個抽象類,將部分邏輯以具體方法以及具體構(gòu)造子的形式實(shí)現(xiàn),然后聲明一些抽象方法來迫使子類實(shí)現(xiàn)剩余的邏輯。不同的子類可以以不同的方式實(shí)現(xiàn)這些抽象方法,從而對剩余的邏輯有不同的實(shí)現(xiàn)。這就是模版方法模式的用意。

很多人可能沒有想到,模版方法模式實(shí)際上是所有模式中最為常見的幾個模式之一,而且很多人可能使用過模版方法模式而沒有意識到自己已經(jīng)使用了這個模式。模版方法模式是基于繼承的代碼復(fù)用的基本技術(shù),模版方法模式的結(jié)構(gòu)和用法也是面向?qū)ο笤O(shè)計(jì)的核心。

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


二、 模版方法模式的結(jié)構(gòu)

模版方法模式的靜態(tài)結(jié)構(gòu)如下圖所示。

 

這里涉及到兩個角色:

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

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

定義并實(shí)現(xiàn)了一個模版方法。這個模版方法一般是一個具體方法,它給出了一個頂級邏輯的骨架,而邏輯的組成步驟在相應(yīng)的抽象操作中,推遲到子類實(shí)現(xiàn)。頂級邏輯也有可能調(diào)用一些具體方法。

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

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

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


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

 

// 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()");
  }

}


/// 
/// Client test
/// 

public class Client
{
  
public static void Main( string[] args )
  
{
    
// Create instance and call template method
    ConcreteClass c = new ConcreteClass();
    c.TemplateMethod();
  }

}

 


四、 繼承作為復(fù)用的工具

使用繼承作為復(fù)用的手段必須慎重,C#語言的設(shè)計(jì)師對使用繼承作為復(fù)用的工具有著不同層次上的認(rèn)識。

不知其一

首先,初學(xué)C#的程序員可能不知道什么是繼承,或者認(rèn)為"繼承"是高深的工具。那時候,大部分的功能復(fù)用都是通過委派進(jìn)行的。

知其一、不知其二

然后慢慢地,他們發(fā)現(xiàn)在C#語言里實(shí)現(xiàn)繼承并不困難,并且初步認(rèn)識到繼承可以使子類一下子得到基類的行為。這時他們就會躍躍欲試了,試圖使用繼承作為功能復(fù)用的主要工具,并把原來應(yīng)當(dāng)使用委派的地方,改為使用繼承,這時繼承就有被濫用的危險。

知其二

很多面向?qū)ο蟮脑O(shè)計(jì)專家從1986年就開始警告繼承關(guān)系被濫用的可能。有一些面向?qū)ο蟮木幊陶Z言,如SELF語言,甚至將類的繼承關(guān)系從語言的功能中取消掉,改為完全使用委派。

其他的設(shè)計(jì)師雖然不提倡徹底取消繼承,但無一例外地鼓勵在設(shè)計(jì)中盡可能使甩委派關(guān)系代替繼承關(guān)系。比如在【GOF95】一書中,狀態(tài)模式、策略模 式、裝飾模式、橋梁模式以及抽象工廠模式均是將依賴于繼承的實(shí)現(xiàn)轉(zhuǎn)換為基于對象的組合和聚合的實(shí)現(xiàn),這些模式的要點(diǎn)就是使用委派關(guān)系代替繼承關(guān)系。

知其三

是不是繼承就根本不該使用呢?事實(shí)上對數(shù)據(jù)的抽象化、繼承、封裝和多態(tài)性并稱C#和其他絕大多數(shù)的面向?qū)ο笳Z言的幾項(xiàng)最重要的特性。繼承不應(yīng)當(dāng)被濫用,并不意味著繼承根本就不該使用。因?yàn)槔^承容易被濫用就徹底拋棄繼承,無異于因噎廢食。

繼承使得類型的等級結(jié)構(gòu)易于理解、維護(hù)和擴(kuò)展,而類型的等級結(jié)構(gòu)非常適合于抽象化的設(shè)計(jì)、實(shí)現(xiàn)和復(fù)用。盡管【GOF95】所給出的設(shè)計(jì)模式基本上沒 有太多基于繼承的模式,很多模式都是用繼承的辦法定義、實(shí)現(xiàn)接口的。多數(shù)的設(shè)計(jì)模式都描寫一個以抽象類作為基類,以具體類作為實(shí)現(xiàn)的等級結(jié)構(gòu),比如適配器 模式、合成模式、橋梁模式、狀態(tài)模式等。

模版方法模式則更進(jìn)了一步:此模式鼓勵恰當(dāng)?shù)厥褂美^承。此模式可以用來改寫一些擁有相同功能的相關(guān)的類,將可復(fù)用的一般性的行為代碼移到基類里面,而把特殊化的行為代碼移到子類里面。

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


五、 一個實(shí)際應(yīng)用模板方法的例子

下面的例子演示了數(shù)據(jù)庫訪問的模板方法。實(shí)際應(yīng)用時,請確保C盤根目錄下有nwind.mdb這個Access數(shù)據(jù)庫(可以從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
  }

}


/// 
///  TemplateMethodApp test
/// 

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)。

抽象方法:一個抽象方法由抽象類聲明,由具體子類實(shí)現(xiàn)。在C#語言里一個抽象方法以abstract關(guān)鍵字標(biāo)示出來。

具體方法:一個具體方法由抽象類聲明并實(shí)現(xiàn),而子類并不實(shí)現(xiàn)或置換。在C#語言里面,一個具體方法沒有abstract關(guān)鍵字。

鉤子方法:一個鉤子方法由抽象類聲明并實(shí)現(xiàn),而子類會加以擴(kuò)展。通常抽象類給出的實(shí)現(xiàn)是一個空實(shí)現(xiàn),作為方法的默認(rèn)實(shí)現(xiàn)。(Visual FoxPro中項(xiàng)目向?qū)Ы⒌捻?xiàng)目會使用一個AppHook類實(shí)現(xiàn)監(jiān)視項(xiàng)目成員變化,調(diào)整系統(tǒng)結(jié)構(gòu)的工作。)鉤子方法的名字通常以do開始。


七、 重構(gòu)的原則

在對一個繼承的等級結(jié)構(gòu)做重構(gòu)時,一個應(yīng)當(dāng)遵從的原則便是將行為盡量移動到結(jié)構(gòu)的高端,而將狀態(tài)盡量移動到結(jié)構(gòu)的低端。

1995年,Auer曾在文獻(xiàn)【AUER95】中指出:

  1. 應(yīng)當(dāng)根據(jù)行為而不是狀態(tài)定義一個類。也就是說,一個類的實(shí)現(xiàn)首先建立在行為的基礎(chǔ)之上,而不是建立在狀態(tài)的基礎(chǔ)之上。
  2. 在實(shí)現(xiàn)行為時,是用抽象狀態(tài)而不是用具體狀態(tài)。如果一個行為涉及到對象的狀態(tài)時,使用間接的引用而不是直接的引用。換言之,應(yīng)當(dāng)使用取值方法而不是直接引用屬性。
  3. 給操作劃分層次。一個類的行為應(yīng)當(dāng)放到一個小組核心方法(Kernel Methods)里面,這些方法可以很方便地在子類中加以置換。
  4.  將狀態(tài)屬性的確認(rèn)推遲到子類中。不要在抽象類中過早地聲明屬性變量,應(yīng)將它們盡量地推遲到子類中去聲明。在抽象超類中,如果需要狀態(tài)屬性的話,可以調(diào)用抽象的取值方法,而將抽象的取值方法的實(shí)現(xiàn)放到具體子類中。

如果能夠遵從這樣的原則,那么就可以在等級結(jié)構(gòu)中將接口與實(shí)現(xiàn)分隔開來,將抽象與具體分割開來,從而保證代碼可以最大限度地被復(fù)用。這個過程實(shí)際上是將設(shè)計(jì)師引導(dǎo)到模版方法模式上去。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多