意图/定义:定义一个操作算法的骨架,将一些步骤推迟到子类中实现。使得子类可以不改变算法的结构而重定义此算法的步骤
问题:要完成在某一细节层次一致的一个过程或一系列步骤,全其个别步骤在更详细的层次上实现可能不同
解决方案:允许定义可变的子步骤,同时保持基本过程一致
参与者与协作者:模板方法由一个抽象类组成,这个抽象类定义了需要覆盖的基本方法,每个从这个抽象类派生的具体类将为此模板实现新方法。
效果:模板提供了一个比较好的代码利用平台。它还有助于确保所需步骤的实现。它将每个具体类的覆盖步骤绑定起来,因此只有在这些变化总是并且只能一起发生时,才应用使用模板方法。
实现:创建一人抽象类,用抽象方法实现一个过程。这些抽象方法必须在子类中实现,以执行过程的每一个步骤。如果这些步骤是独立变化的,则每个步骤都可以用策略模式来实现。
下面是一个模板方法的示例,演示了两个访问数据库操作的两个具体类,两个类在基类中实现了共同的操作如打开连接、关闭连接,但在这个连接上具体选择哪个表及做什么处理是由基类实现的。
using System;
using System.Data;
using System.Data.OleDb;
namespace Template
{
/// <summary>
/// MainApp startup class for Real-World
/// Template Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
static void Main()
{
DataAccessObject daoCategories = new Categories();
daoCategories.Run();
DataAccessObject daoProducts = new Products();
daoProducts.Run();
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'AbstractClass' abstract class
/// </summary>
abstract class DataAccessObject
{
protected string connectionString;
protected DataSet dataSet;
public virtual void Connect()
{
// Make sure mdb is available to app
connectionString =
"provider=Microsoft.JET.OLEDB.4.0; " +
"data source=..\\..\\..\\db1.mdb";
}
public abstract void Select();
public abstract void Process();
public virtual void Disconnect()
{
connectionString = "";
}
// The 'Template Method'
public void Run()
{
Connect();
Select();
Process();
Disconnect();
}
}
/// <summary>
/// A 'ConcreteClass' class
/// </summary>
class Categories : DataAccessObject
{
public override void Select()
{
string sql = "select CategoryName from Categories";
OleDbDataAdapter dataAdapter = new OleDbDataAdapter(sql, connectionString);
dataSet = new DataSet();
dataAdapter.Fill(dataSet, "Categories");
}
public override void Process()
{
Console.WriteLine("Categories ---- ");
DataTable dataTable = dataSet.Tables["Categories"];
foreach (DataRow row in dataTable.Rows)
{
Console.WriteLine(row["CategoryName"]);
}
Console.WriteLine();
}
}
/// <summary>
/// A 'ConcreteClass' class
/// </summary>
class Products : DataAccessObject
{
public override void Select()
{
string sql = "select ProductName from Products";
OleDbDataAdapter dataAdapter = new OleDbDataAdapter(
sql, connectionString);
dataSet = new DataSet();
dataAdapter.Fill(dataSet, "Products");
}
public override void Process()
{
Console.WriteLine("Products ---- ");
DataTable dataTable = dataSet.Tables["Products"];
foreach (DataRow row in dataTable.Rows)
{
Console.WriteLine(row["ProductName"]);
}
Console.WriteLine();
}
}
}