C#設計模式-模板方法模式(Template Method)

概念

模板指一些可以套用的公共內容,例如網頁模板是當網站中有許多頁面版式色彩相同的情況下,將其定義為網頁模板,並定義其中部分可編輯,部分不可編輯,那麼在利用網頁模板製作其他頁面時就會很方便,不易出錯。
在設計模式中,模板方法模式中模板和生活中模板概念非常類似,在一個抽象類中定義一個操作中的算法骨架(對應於模板),而將一些步驟延遲到子類中去實現(對應根據自己的情況向模板填充內容)。
在面向對象程序設計過程中,程序員常常會遇到這種情況:設計一個系統時知道了算法所需的關鍵步驟,而且確定了這些步驟的執行順序,但某些步驟的具體實現還未知,或者說某些步驟的實現與具體的環境相關。此時就可以採用模板方法進行設計。

例如公司的入職流程,進入公司,入職準備、入職報到、辦理入職手續、進行入職培訓,轉正,入職結束進入崗位。這些步驟都很固定,但是不同的公司,流程中每個步驟稍有不同。這些不同的可以在具體實現上進行填充。

特點

優點:
它封裝了不變部分,擴展可變部分。它把認為是不變部分的算法封裝到父類中實現,而把可變部分算法由子類繼承實現,便於子類繼續擴展。
它在父類中提取了公共的部分代碼,實現了代碼復用。
部分方法是由子類實現的,因此子類可以通過擴展方式增加相應的功能,符合開閉原則。

缺點:
對每個不同的實現都需要定義一個子類,這會導致類的個數增加,系統更加龐大,設計也更加抽象。
父類中的抽象方法由子類實現,子類執行的結果會影響父類的結果,這導致一種反向的控制結構,它提高了代碼閱讀的難度。
因為引入了一個抽象類,如果具體實現過多的話,需要用戶或開發人員需要花更多的時間去理清類之間的關係。

模式結構

根據模板方法模式類圖結構,有利於我們理清該模式中類之間的關係,具體類圖如下:

模板方法模式中涉及的角色:
抽象模板角色:定義了一個或多個抽象操作,以便讓子類實現,這些抽象操作稱為基本操作。它由一個模板方法和若干個基本方法構成。
  模板方法:定義了算法的骨架,按某種順序調用其包含的基本方法。
  基本方法:是整個算法中的一個步驟,包含了抽象方法、具體方法。
具體模板角色:實現父類所定義的一個或多個抽象方法。

應用場景

算法的整體步驟很固定,但其中個別部分易變時,這時候可以使用模板方法模式,將容易變的部分抽象出來,供子類實現。
當多個子類存在公共的行為時,可以將其提取出來並集中到一個公共父類中以避免代碼重複。首先,要識別現有代碼中的不同之處,並且將不同之處分離為新的操作。最後,用一個調用這些新的操作的模板方法來替換這些不同的代碼。

實現

以入職流程為例,進行具體的實現。

using System;

namespace 模板模式
{
    class EntryProcess
    {
        static void Main(string[] args)
        {
            SamsungEntryProcess samsung = new SamsungEntryProcess();
            HuaweiEntryProcess huawei = new HuaweiEntryProcess();

            samsung.JoiningCompany();
            huawei.JoiningCompany();

            Console.Read();
        }
    }

    // 抽象類,入職流程
    public abstract class TemplateEntryProcess
    {
        // 模板方法,不要把模版方法定義為 Virtual 或 abstract 方法,避免被子類重寫,防止更改流程的執行順序
        public void JoiningCompany()
        {
            this.entryCompany(); // 進入公司
            this.preparationForEntry(); // 入職前準備,整理衣帽等
            this.registrationForEmployment(); // 入職報到
            this.entryProcedures(); // 辦理入職手續
            this.inductionTraining(); // 入職培訓
            this.evaluationOfConversion(); // 轉正評估
            this.entryOver(); // 入職結束,進入崗位
        }

        public abstract void entryCompany();

        public void preparationForEntry()
        {
            Console.WriteLine("做準備,整理衣帽等;");
        }

        public void registrationForEmployment()
        {
            Console.WriteLine("入職報到;");
        }
        public void entryProcedures()
        {
            Console.WriteLine("辦理入職手續;");
        }
        public void inductionTraining()
        {
            Console.WriteLine("進行入職培訓;");
        }
        public void evaluationOfConversion()
        {
            Console.WriteLine("完成入職前培訓,進行轉正評估;");
        }
        public void entryOver()
        {
            Console.WriteLine("入職流程完成,進入崗位。");
        }

    }

    // 具體子類,三星入職
    public class SamsungEntryProcess: TemplateEntryProcess
    {
        public override void entryCompany()
        {
            Console.WriteLine("進入三星公司");
        }

    }

    // 具體子類,華為入職
    public class HuaweiEntryProcess: TemplateEntryProcess
    {
        public override void entryCompany()
        {
            Console.WriteLine("進入華為公司");
        }
    }
}

執行後結果

進入三星公司
做準備,整理衣帽等;
入職報到;
辦理入職手續;
進行入職培訓;
完成入職前培訓,進行轉正評估;
入職流程完成,進入崗位。
進入華為公司
做準備,整理衣帽等;
入職報到;
辦理入職手續;
進行入職培訓;
完成入職前培訓,進行轉正評估;
入職流程完成,進入崗位。