Template Method Design Pattern
UML class diagram
participants
The classes and/or objects participating in this pattern are:
- AbstractClass (DataObject)
- defines abstract primitive operations that concrete subclasses define to implement steps of an algorithm
- implements a template method defining the skeleton of an algorithm. The template method calls primitive operations as well as operations defined in AbstractClass or those of other objects.
- ConcreteClass (CustomerDataObject)
- implements the primitive operations ot carry out subclass-specific steps of the algorithm
sample code in C#
This structural code demonstrates the Template method which provides a skeleton calling sequence of methods. One or more steps can be deferred to subclasses which implement these steps without changing the overall calling sequence.
Hide code
// Template Method pattern -- Structural example
|
using System;
namespace DoFactory.GangOfFour.Template.Structural
{
/// <summary>
/// MainApp startup class for Real-World
/// Template Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
static void Main()
{
AbstractClass aA = new ConcreteClassA();
aA.TemplateMethod();
AbstractClass aB = new ConcreteClassB();
aB.TemplateMethod();
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'AbstractClass' abstract class
/// </summary>
abstract class AbstractClass
{
public abstract void PrimitiveOperation1();
public abstract void PrimitiveOperation2();
// The "Template method"
public void TemplateMethod()
{
PrimitiveOperation1();
PrimitiveOperation2();
Console.WriteLine("");
}
}
/// <summary>
/// A 'ConcreteClass' class
/// </summary>
class ConcreteClassA : AbstractClass
{
public override void PrimitiveOperation1()
{
Console.WriteLine("ConcreteClassA.PrimitiveOperation1()");
}
public override void PrimitiveOperation2()
{
Console.WriteLine("ConcreteClassA.PrimitiveOperation2()");
}
}
/// <summary>
/// A 'ConcreteClass' class
/// </summary>
class ConcreteClassB : AbstractClass
{
public override void PrimitiveOperation1()
{
Console.WriteLine("ConcreteClassB.PrimitiveOperation1()");
}
public override void PrimitiveOperation2()
{
Console.WriteLine("ConcreteClassB.PrimitiveOperation2()");
}
}
}
|
Output
ConcreteClassA.PrimitiveOperation1()
ConcreteClassA.PrimitiveOperation2() |
This real-world code demonstrates a Template method named Run() which provides a skeleton calling sequence of methods. Implementation of these steps are deferred to the CustomerDataObject subclass which implements the Connect, Select, Process, and Disconnect methods.
Hide code
// Template Method pattern -- Real World example
|
using System;
using System.Data;
using System.Data.OleDb;
namespace DoFactory.GangOfFour.Template.RealWorld
{
/// <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();
}
}
}
|
Output
Categories ----
Beverages Condiments Confections Dairy Products Grains/Cereals Meat/Poultry Produce Seafood Products ---- Chai Chang Aniseed Syrup Chef Anton's Cajun Seasoning Chef Anton's Gumbo Mix Grandma's Boysenberry Spread Uncle Bob's Organic Dried Pears Northwoods Cranberry Sauce Mishi Kobe Niku |
This .NET optimized code demonstrates the same real-world situation as above but uses modern, built-in .NET features, such as, generics, reflection, object initializers, automatic properties, etc.
No comments:
Post a Comment