 |
Define the skeleton of an algorithm in an operation, deferring some steps
to subclasses. Template Method lets subclasses redefine certain steps
of an algorithm without changing the algorithm's structure.
Frequency of use: nbsp;medium
|
|
 |
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
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.
Show code
|
// Template pattern -- Structural example
|
|
using System;
namespace DoFactory.GangOfFour.Template.Structural
{
// MainApp test application
class MainApp
{
static void Main()
{
AbstractClass c;
c = new ConcreteClassA();
c.TemplateMethod();
c = new ConcreteClassB();
c.TemplateMethod();
// Wait for user
Console.Read();
}
}
// "AbstractClass"
abstract class AbstractClass
{
public abstract void PrimitiveOperation1();
public abstract void PrimitiveOperation2();
// The "Template method"
public void TemplateMethod()
{
PrimitiveOperation1();
PrimitiveOperation2();
Console.WriteLine("");
}
}
// "ConcreteClass"
class ConcreteClassA : AbstractClass
{
public override void PrimitiveOperation1()
{
Console.WriteLine("ConcreteClassA.PrimitiveOperation1()");
}
public override void PrimitiveOperation2()
{
Console.WriteLine("ConcreteClassA.PrimitiveOperation2()");
}
}
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.
Show code
|
// Template pattern -- Real World example
|
|
using System;
using System.Data;
using System.Data.OleDb;
namespace DoFactory.GangOfFour.Template.RealWorld
{
// MainApp test application
class MainApp
{
static void Main()
{
DataAccessObject dao;
dao = new Categories();
dao.Run();
dao = new Products();
dao.Run();
// Wait for user
Console.Read();
}
}
// "AbstractClass"
abstract class DataAccessObject
{
protected string connectionString;
protected DataSet dataSet;
public virtual void Connect()
{
// Make sure mdb is on c:\
connectionString =
"provider=Microsoft.JET.OLEDB.4.0; " +
"data source=c:\\nwind.mdb";
}
public abstract void Select();
public abstract void Process();
public virtual void Disconnect()
{
connectionString = "";
}
// The "Template Method"
public void Run()
{
Connect();
Select();
Process();
Disconnect();
}
}
// "ConcreteClass"
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();
}
}
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.
Show code
|
// Template pattern -- .NET optimized
|
See our Singleton page for a .NET optimized code sample.
|
|
|