Шаблонный метод (Template method)
Паттерн Шаблонный Метод задает “скелет” алгоритма в методе, оставляя определение реализации некоторых шагов субклассам. Субклассы могут переопределять некоторые части алгоритма без изменения его структуры.
-
AbstractClass - абстрактный класс
-
ConcreteClass - конкретный класс
Абстрактная реализация на C# (GoF)
/// <summary>
/// MainApp startup class for Real-World
/// Template Design Pattern.
/// </summary>
internal class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
private 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>
internal 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>
internal class ConcreteClassA : AbstractClass
{
public override void PrimitiveOperation1()
{
Console.WriteLine("ConcreteClassA.PrimitiveOperation1()");
}
public override void PrimitiveOperation2()
{
Console.WriteLine("ConcreteClassA.PrimitiveOperation2()");
}
}
/// <summary>
/// A 'ConcreteClass' class
/// </summary>
internal class ConcreteClassB : AbstractClass
{
public override void PrimitiveOperation1()
{
Console.WriteLine("ConcreteClassB.PrimitiveOperation1()");
}
public override void PrimitiveOperation2()
{
Console.WriteLine("ConcreteClassB.PrimitiveOperation2()");
}
}
Реальная реализация на C# (GoF)
/// <summary>
/// MainApp startup class for Real-World
/// Template Design Pattern.
/// </summary>
internal class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
private 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>
internal 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>
internal 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>
internal 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();
}
}
Улучшенная реальная реализация на C# (GoF)
/// <summary>
/// MainApp startup class for .NET optimized
/// Template Design Pattern.
/// </summary>
internal class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
private static void Main()
{
var daoCategories = new Categories();
daoCategories.Run();
var daoProducts = new Products();
daoProducts.Run();
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'AbstractClass' abstract class
/// </summary>
internal 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();
virtual public void Disconnect()
{
connectionString = "";
}
// The 'Template Method'
public void Run()
{
Connect();
Select();
Process();
Disconnect();
}
}
/// <summary>
/// A 'ConcreteClass' class
/// </summary>
internal class Categories : DataAccessObject
{
public override void Select()
{
string sql = "SELECT CategoryName FROM Categories";
var dataAdapter = new OleDbDataAdapter(sql, connectionString);
dataSet = new DataSet();
dataAdapter.Fill(dataSet, "Categories");
}
public override void Process()
{
Console.WriteLine("Categories ---- ");
var dataTable = dataSet.Tables["Categories"];
foreach (DataRow row in dataTable.Rows)
{
Console.WriteLine(row["CategoryName"]);
}
Console.WriteLine();
}
}
/// <summary>
/// A 'ConcreteClass' class
/// </summary>
internal class Products : DataAccessObject
{
public override void Select()
{
string sql = "SELECT ProductName FROM Products";
var dataAdapter = new OleDbDataAdapter(
sql, connectionString);
dataSet = new DataSet();
dataAdapter.Fill(dataSet, "Products");
}
public override void Process()
{
Console.WriteLine("Products ---- ");
var dataTable = dataSet.Tables["Products"];
foreach (DataRow row in dataTable.Rows)
{
Console.WriteLine(row["ProductName"]);
}
Console.WriteLine();
}
}
Реализация на C# (Head First)
internal class BeverageTestDrive
{
private static void Main(string[] args)
{
Console.WriteLine("\nMaking tea...");
var tea = new Tea();
tea.PrepareRecipe();
Console.WriteLine("\nMaking coffee...");
var coffee = new Coffee();
coffee.PrepareRecipe();
// Hooked on Template (page 292)
Console.WriteLine("\nMaking tea...");
var teaHook = new TeaWithHook();
teaHook.PrepareRecipe();
Console.WriteLine("\nMaking coffee...");
var coffeeHook = new CoffeeWithHook();
coffeeHook.PrepareRecipe();
// Wait for user
Console.ReadKey();
}
}
#region Coffee and Tea
public abstract class CaffeineBeverage
{
public void PrepareRecipe()
{
BoilWater();
Brew();
PourInCup();
AddCondiments();
}
public abstract void Brew();
public abstract void AddCondiments();
private void BoilWater()
{
Console.WriteLine("Boiling water");
}
private void PourInCup()
{
Console.WriteLine("Pouring into cup");
}
}
public class Coffee : CaffeineBeverage
{
public override void Brew()
{
Console.WriteLine("Dripping Coffee through filter");
}
public override void AddCondiments()
{
Console.WriteLine("Adding Sugar and Milk");
}
}
public class Tea : CaffeineBeverage
{
public override void Brew()
{
Console.WriteLine("Steeping the tea");
}
public override void AddCondiments()
{
Console.WriteLine("Adding Lemon");
}
}
#endregion Coffee and Tea
#region Coffee and Tea with Hook
public abstract class CaffeineBeverageWithHook
{
public void PrepareRecipe()
{
BoilWater();
Brew();
PourInCup();
if (CustomerWantsCondiments())
{
AddCondiments();
}
}
public abstract void Brew();
public abstract void AddCondiments();
public void BoilWater()
{
Console.WriteLine("Boiling water");
}
public void PourInCup()
{
Console.WriteLine("Pouring into cup");
}
public virtual bool CustomerWantsCondiments()
{
return true;
}
}
public class CoffeeWithHook : CaffeineBeverageWithHook
{
public override void Brew()
{
Console.WriteLine("Dripping Coffee through filter");
}
public override void AddCondiments()
{
Console.WriteLine("Adding Sugar and Milk");
}
public override bool CustomerWantsCondiments()
{
string answer = GetUserInput();
if (answer.ToLower().StartsWith("y"))
{
return true;
}
else
{
return false;
}
}
public string GetUserInput()
{
string answer = null;
Console.WriteLine("Would you like milk and sugar with your coffee (y/n)? ");
try
{
answer = Console.ReadLine();
}
catch
{
Console.WriteLine("IO error trying to read your answer");
}
if (answer == null)
{
return "no";
}
return answer;
}
}
public class TeaWithHook : CaffeineBeverageWithHook
{
public override void Brew()
{
Console.WriteLine("Steeping the tea");
}
public override void AddCondiments()
{
Console.WriteLine("Adding Lemon");
}
public override bool CustomerWantsCondiments()
{
string answer = GetUserInput();
if (answer.ToLower().StartsWith("y"))
{
return true;
}
else
{
return false;
}
}
private string GetUserInput()
{
// get the user's response
string answer = null;
Console.WriteLine("Would you like lemon with your tea (y/n)? ");
try
{
answer = Console.ReadLine();
}
catch
{
Console.WriteLine("IO error trying to read your answer");
}
if (answer == null)
{
return "no";
}
return answer;
}
}
#endregion Coffee and Tea with Hook
Реализация на JAVA
TODO