1. 程式人生 > 實用技巧 >行為型模式之模板方法

行為型模式之模板方法

目錄

在面向物件程式設計過程中,程式設計師常常會遇到這種情況:設計一個系統時知道了演算法所需的關鍵步驟,而且確定了這些步驟的執行順序,但某些步驟的具體實現還未知,或者說某些步驟的實現與具體的環境相關。
例如,一個人每天會起床、吃飯、做事、睡覺等,其中“做事”的內容每天可能不同。我們把這些規定了流程或格式的例項定義成模板,允許使用者根據自己的需求去更新它,例如,簡歷模板、論文模板、Word 中模板檔案等。

定義與特點

模板方法(Template Method)模式的定義如下:定義一個操作中的演算法骨架,而將演算法的一些步驟延遲到子類中,使得子類可以不改變該演算法結構的情況下重定義該演算法的某些特定步驟。它是一種類行為型模式。

該模式的主要優點如下:

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

該模式的主要缺點如下:

  • 對每個不同的實現都需要定義一個子類,這會導致類的個數增加,系統更加龐大,設計也更加抽象
  • 父類中的抽象方法由子類實現,子類執行的結果會影響父類的結果,這導致一種反向的控制結構,它提高了程式碼閱讀的難度

結構與實現

模板方法模式需要注意抽象類與具體子類之間的協作。它用到了虛擬函式的多型性技術以及“不用呼叫我,讓我來呼叫你”的反向控制技術

模式的結構

模板方法模式包含以下主要角色:

抽象類(Abstract Class):負責給出一個演算法的輪廓和骨架。它由一個模板方法和若干個基本方法構成,這些方法的定義如下:

  • 模板方法:定義了演算法的骨架,按某種順序呼叫其包含的基本方法。
  • 基本方法:是整個演算法中的一個步驟,包含以下幾種型別:
  • 抽象方法:在抽象類中申明,由具體子類實現
  • 具體方法:在抽象類中已經實現,在具體子類中可以繼承或重寫它
  • 鉤子方法:在抽象類中已經實現,包括用於判斷的邏輯方法和需要子類重寫的空方法兩種

具體子類(Concrete Class):實現抽象類中所定義的抽象方法和鉤子方法,它們是一個頂級邏輯的一個組成步驟。

模板方法模式的結構圖如圖所示:

模式的實現

模板方法模式的程式碼如下:

class Program
{    
    static void Main(string[] args)
    {
        AbstractClass tm=new ConcreteClass();
        tm.TemplateMethod();
        
        Console.Read();
    }
}

//抽象類
public abstract class AbstractClass
{
    public void TemplateMethod() //模板方法
    {
        SpecificMethod();
        AbstractMethod1();          
        AbstractMethod2();
    }  
    public void SpecificMethod() //具體方法
    {
        Console.WriteLine("抽象類中的具體方法被呼叫...");
    }   
    public abstract void AbstractMethod1(); //抽象方法1
    public abstract void AbstractMethod2(); //抽象方法2
}

//具體子類
public class ConcreteClass : AbstractClass
{
    public override void AbstractMethod1()
    {
        Console.WriteLine("抽象方法1的實現被呼叫...");
    }
    public override void AbstractMethod2()
    {
        Console.WriteLine("抽象方法2的實現被呼叫...");
    }
}

程式的執行結果如下:

抽象類中的具體方法被呼叫...
抽象方法1的實現被呼叫...
抽象方法2的實現被呼叫...

應用場景

模板方法模式通常適用於以下場景:

  • 演算法的整體步驟很固定,但其中個別部分易變時,這時候可以使用模板方法模式,將容易變的部分抽象出來,供子類實現。
  • 當多個子類存在公共的行為時,可以將其提取出來並集中到一個公共父類中以避免程式碼重複。首先,要識別現有程式碼中的不同之處,並且將不同之處分離為新的操作。最後,用一個呼叫這些新的操作的模板方法來替換這些不同的程式碼。
  • 當需要控制子類的擴充套件時,模板方法只在特定點呼叫鉤子操作,這樣就只允許在這些點進行擴充套件

擴充套件:重寫鉤子方法

在模板方法模式中,基本方法包含:抽象方法、具體方法和鉤子方法,正確使用“鉤子方法”可以使得子類控制父類的行為。如下面例子中,可以通過在具體子類中重寫鉤子方法 HookMethod1() 和 HookMethod2() 來改變抽象父類中的執行結果,其結構圖如圖所示:

程式程式碼如下:

class Program
{    
    static void Main(string[] args)
    {
        HookAbstractClass tm = new HookConcreteClass();
        tm.TemplateMethod();
        
        Console.Read();
    }
}

//含鉤子方法的抽象類
public abstract class HookAbstractClass
{
    public void TemplateMethod() //模板方法
    {
        AbstractMethod1();
        HookMethod1();
        if(HookMethod2())
        {
            SpecificMethod();   
        }
         AbstractMethod2();
    }  
    public void SpecificMethod() //具體方法
    {
        Console.WriteLine("抽象類中的具體方法被呼叫...");
    }
    public virtual void HookMethod1(){}  //鉤子方法1
    public virtual bool HookMethod2() //鉤子方法2
    {
        return true;
    }
    public abstract void AbstractMethod1(); //抽象方法1
    public abstract void AbstractMethod2(); //抽象方法2
}

//含鉤子方法的具體子類
public class HookConcreteClass : HookAbstractClass
{
    public override void AbstractMethod1()
    {
        Console.WriteLine("抽象方法1的實現被呼叫...");
    }
    public override void AbstractMethod2()
    {
        Console.WriteLine("抽象方法2的實現被呼叫...");
    }
    public override void HookMethod1()
    {
        Console.WriteLine("鉤子方法1被重寫...");
    }
    public override bool HookMethod2()
    {
        return false;
    }
}