الگوی Template Method

الگوی Template Method(متد قلابدار-متد قالب) در دسته الگوهای رفتاری قرار دارد. اسکلت یک الگوریتم در یک عملیات را تعریف می کند، بعضی مراحل را به زیر کلاس ها منتقل می کند.Template Method اجازه می دهد که زیر کلاس ها گام های خاصی از یک الگوریتم را دوباره تغییر دهند بدون تغییر ساختار الگوریتم.به عبارت دیگر اسکلت کلی الگوریتم را تعریف کرده و این امکان را فراهم می‌کند که بدون تغییر ساختار کلی الگوریتم بتوان برخی از مراحل آن را به زیر کلاس ها سپرد.UML class diagram شركت كنندگانکلاس ها و اشیاء شرکت کننده در این الگو عبارتند از:کلاس انتزاعی AbstractClass (شی داده در مثال دنیای واقعی DataObject):تعریف عملیات اولیه(عملیات اصلی) انتزاعی را که زیر کلاسهای سفت برای پیاده سازی مراحل الگوریتم تعریف می کنند.یک Template Method(متدقالب) را تعریف اسکلت یک الگوریتم را پیاده سازی می کند.Template Method(متد قالب)، عملیات اولیه(اصلی) و همچنین عملیات تعریف شده در AbstractClass یا همان شیء دیگر را فراخوانی می کند.به عبارت دیگر ()TemplateMethodمتدی که برخی از مراحل آن قرار است توسط زیر کلاس و توسعه داده شود. به عبارت دیگر کلاسی است که Template Method را تعریف کرده است. نکته: PrimitiveOperationX() متدهایی که ()TemplateMethod خودش آنها را صدا میزند.کلاس سفت ConcreteClass (شی داده مشتری CustomerDataObject):عملیات ابتدایی (اصلی - primitive operations) را پیاده سازی می کند تا برای زیرکلاس مخصوص (subclass-specific) مراحل الگوریتم انجام دهد. به عبارت دیگر زیر کلاس ها با پیاده سازی آن متدها به شکل های مختلف می تواند الگوریتم را برای خود سفارشی کنند. ساختار کلی کد متدقالب (Template Method) را نشان می دهد که توالی فراخوانی اسکلت را فراهم می کند. یک یا چند مرحله را می توان به زیر کلاس ها تعویض کرد که این مراحل را بدون تغییر توالی فراخوانی پیاده سازی می کنند. 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()"); } } } OutputConcreteClassA.PrimitiveOperation1 ()ConcreteClassA.PrimitiveOperation2 ()یاabstract class AbstractClass { public void TemplateMethod() { Console.WriteLine("\nAbstractClass.TemplateMethod() started"); PrimitiveOperation1(); PrimitiveOperation2(); Console.WriteLine("AbstractClass.TemplateMethod() ended"); } public abstract void PrimitiveOperation1(); public abstract void PrimitiveOperation2(); } 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()"); } } class Test { public static void Main() { var c1 = new ConcreteClassA(); c1.TemplateMethod(); var c2 = new ConcreteClassB(); c2.TemplateMethod(); Console.ReadKey(); } } /* OUTPUT:AbstractClass.TemplateMethod() startedConcreteClassA.PrimitiveOperation1()ConcreteClassA.PrimitiveOperation2()AbstractClass.TemplateMethod() endedAbstractClass.TemplateMethod() startedConcreteClassB.PrimitiveOperation1()ConcreteClassB.PrimitiveOperation2()AbstractClass.TemplateMethod() ended*/مثال دنیای واقعییک متد قالب به نام Run() ارائه می دهد که توالی فراخوانی اسکلت از متد ها را فراهم می کند.پیاده سازی این مراحل به زیرکلاس CustomerDataObject تعبیه شده است که متدهای Connect، Select، Process، و Disconnect را پیاده سازی می کند.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 (); } } } OutputCategories-- --BeveragesCondimentsConfectionsDairy ProductsGrains / CerealsMeat / PoultryProduceSeafoodProducts-- --ChaiChangAniseed SyrupChef Anton 's Cajun SeasoningChef Anton's Gumbo MixGrandma 's Boysenberry SpreadUncle Bob's Organic Dried PearsNorthwoods Cranberry SauceMishi Kobe Nikuمنبع