知其二
很多面向對象的設計專家從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();
}
}