設計模式詳解——模版方法模式

本篇文章介紹一種設計模式——外觀模式。本篇文章內容參考:《JAVA與模式》之模板方法模式模板方法模式深度解析(三)

一、模版方法模式的定義

模板方法模式是類的行爲模式。準備一個抽象類,將部分邏輯以具體方法以及具體構造函數的形式實現,然後聲明一些抽象方法來迫使子類實現剩餘的邏輯。不同的子類可以以不同的方式實現這些抽象方法,從而對剩餘的邏輯有不同的實現。這就是模板方法模式的用意。

二、模版方法模式的結構

模板方法模式是所有模式中最爲常見的幾個模式之一,是基於繼承的代碼複用的基本技術。

模板方法模式需要開發抽象類和具體子類的設計師之間的協作。一個設計師負責給出一個算法的輪廓和骨架,另一些設計師則負責給出這個算法的各個邏輯步驟。代表這些具體邏輯步驟的方法稱做基本方法(primitive method);而將這些基本方法彙總起來的方法叫做模板方法(template method),這個設計模式的名字就是從此而來。

模板方法所代表的行爲稱爲頂級行爲,其邏輯稱爲頂級邏輯。模板方法模式的靜態結構圖如下所示:


這裏涉及到兩個角色:

抽象模板(Abstract Template)角色有如下責任:

■  定義了一個或多個抽象操作,以便讓子類實現。這些抽象操作叫做基本操作,它們是一個頂級邏輯的組成步驟。

■  定義並實現了一個模板方法。這個模板方法一般是一個具體方法,它給出了一個頂級邏輯的骨架,而邏輯的組成步驟在相應的抽象操作中,推遲到子類實現。頂級邏輯也有可能調用一些具體方法。

具體模板(Concrete Template)角色又如下責任:

■  實現父類所定義的一個或多個抽象方法,它們是一個頂級邏輯的組成步驟。

■  每一個抽象模板角色都可以有任意多個具體模板角色與之對應,而每一個具體模板角色都可以給出這些抽象方法(也就是頂級邏輯的組成步驟)的不同實現,從而使得頂級邏輯的實現各不相同。

抽象模板角色類,abstractMethod()、hookMethod()等基本方法是頂級邏輯的組成步驟,這個頂級邏輯由templateMethod()方法代表。

public abstract class AbstractTemplate {
    /**
     * 模板方法
     */
    public void templateMethod(){
        //調用基本方法
        abstractMethod();
        hookMethod();
        concreteMethod();
    }
    /**
     * 基本方法的聲明(由子類實現)
     */
    protected abstract void abstractMethod();
    /**
     * 基本方法(空方法)
     */
    protected void hookMethod(){}
    /**
     * 基本方法(已經實現)
     */
    private final void concreteMethod(){
        //業務相關的代碼
    }
}

具體模板角色類,實現了父類所聲明的基本方法,abstractMethod()方法所代表的就是強制子類實現的剩餘邏輯,而hookMethod()方法是可選擇實現的邏輯,不是必須實現的。

public class ConcreteTemplate extends AbstractTemplate{
    //基本方法的實現
    @Override
    public void abstractMethod() {
        //業務相關的代碼
    }
    //重寫父類的方法
    @Override
    public void hookMethod() {
        //業務相關的代碼
    }
}

模板模式的關鍵是:子類可以置換掉父類的可變部分,但是子類卻不可以改變模板方法所代表的頂級邏輯。

每當定義一個新的子類時,不要按照控制流程的思路去想,而應當按照“責任”的思路去想。換言之,應當考慮哪些操作是必須置換掉的,哪些操作是可以置換掉的,以及哪些操作是不可以置換掉的。使用模板模式可以使這些責任變得清晰。

三、模板方法模式中的方法

模板方法中的方法可以分爲兩大類:模板方法和基本方法。

模板方法

一個模板方法是定義在抽象類中的,把基本操作方法組合在一起形成一個總算法或一個總行爲的方法。

一個抽象類可以有任意多個模板方法,而不限於一個。每一個模板方法都可以調用任意多個具體方法。

基本方法

基本方法又可以分爲三種:抽象方法(Abstract Method)、具體方法(Concrete Method)和鉤子方法(Hook Method)。

●  抽象方法:一個抽象方法由抽象類聲明,由具體子類實現。在Java語言裏抽象方法以abstract關鍵字標示。

●  具體方法:一個具體方法由抽象類聲明並實現,而子類並不實現或置換。

●  鉤子方法:一個鉤子方法由抽象類聲明並實現,而子類會加以擴展。通常抽象類給出的實現是一個空實現,作爲方法的默認實現。

在上面的例子中,AbstractTemplate是一個抽象類,它帶有三個方法。其中abstractMethod()是一個抽象方法,它由抽象類聲明爲抽象方法,並由子類實現;hookMethod()是一個鉤子方法,它由抽象類聲明並提供默認實現,並且由子類置換掉。concreteMethod()是一個具體方法,它由抽象類聲明並實現。

默認鉤子方法

一個鉤子方法常常由抽象類給出一個空實現作爲此方法的默認實現。這種空的鉤子方法叫做“Do Nothing Hook”。具體模版類中可以選擇是否重寫鉤子方法,通常重寫鉤子方法是爲了對模版方法中的步驟進行控制,判斷鉤子方法中的狀態,是否進行下一步操作。

四、模版方法的具體實例

考慮一個計算存款利息的例子。假設系統需要支持兩種存款賬號,即貨幣市場(Money Market)賬號和定期存款(Certificate of Deposite)賬號。這兩種賬號的存款利息是不同的,因此,在計算一個存戶的存款利息額時,必須區分兩種不同的賬號類型。

這個系統的總行爲應當是計算出利息,這也就決定了作爲一個模板方法模式的頂級邏輯應當是利息計算。由於利息計算涉及到兩個步驟:一個基本方法給出賬號種類,另一個基本方法給出利息百分比。這兩個基本方法構成具體邏輯,因爲賬號的類型不同,所以具體邏輯會有所不同。

顯然,系統需要一個抽象角色給出頂級行爲的實現,而將兩個作爲細節步驟的基本方法留給具體子類實現。由於需要考慮的賬號有兩種:一是貨幣市場賬號,二是定期存款賬號。系統的類結構如下圖所示。


抽象模板角色類

public abstract class Account {
    /**
     * 模板方法,計算利息數額
     * @return    返回利息數額
     */
    public final double calculateInterest(){
        double interestRate = doCalculateInterestRate();
        String accountType = doCalculateAccountType();
        double amount = calculateAmount(accountType);
        return amount * interestRate;
    }
    /**
     * 基本方法留給子類實現
     */
    protected abstract String doCalculateAccountType();
    /**
     * 基本方法留給子類實現
     */
    protected abstract double doCalculateInterestRate();
    /**
     * 基本方法,已經實現
     */
    private double calculateAmount(String accountType){
        /**
         * 省略相關的業務邏輯
         */
        return 7243.00;
    }
}

具體模板角色類

public class MoneyMarketAccount extends Account {

    @Override
    protected String doCalculateAccountType() {

        return "Money Market";
    }

    @Override
    protected double doCalculateInterestRate() {

        return 0.045;
    }

}
public class CDAccount extends Account {

    @Override
    protected String doCalculateAccountType() {
        return "Certificate of Deposite";
    }

    @Override
    protected double doCalculateInterestRate() {
        return 0.06;
    }

}

客戶端類

public class Client {

    public static void main(String[] args) {
        Account account = new MoneyMarketAccount();
        System.out.println("貨幣市場賬號的利息數額爲:" + account.calculateInterest());
        account = new CDAccount();
        System.out.println("定期賬號的利息數額爲:" + account.calculateInterest());
    }

}

五、模板方法模式效果與適用場景

模板方法模式是基於繼承的代碼複用技術,它體現了面向對象的諸多重要思想,是一種使用較爲頻繁的模式。模板方法模式廣泛應用於框架設計中,以確保通過父類來控制處理流程的邏輯順序(如框架的初始化,測試流程的設置等)。

在以下情況下可以考慮使用模板方法模式:

(1) 對一些複雜的算法進行分割,將其算法中固定不變的部分設計爲模板方法和父類具體方法,而一些可以改變的細節由其子類來實現。即:一次性實現一個算法的不變部分,並將可變的行爲留給子類來實現。

(2) 各子類中公共的行爲應被提取出來並集中到一個公共父類中以避免代碼重複。

(3) 需要通過子類來決定父類算法中某個步驟是否執行,實現子類對父類的反向控制。

六、模版方法模式的優缺點

優點

(1) 在父類中形式化地定義一個算法,而由它的子類來實現細節的處理,在子類實現詳細的處理算法時並不會改變算法中步驟的執行次序。

(2) 模板方法模式是一種代碼複用技術,它在類庫設計中尤爲重要,它提取了類庫中的公共行爲,將公共行爲放在父類中,而通過其子類來實現不同的行爲,它鼓勵我們恰當使用繼承來實現代碼複用。

(3) 可實現一種反向控制結構,通過子類覆蓋父類的鉤子方法來決定某一特定步驟是否需要執行。

(4) 在模板方法模式中可以通過子類來覆蓋父類的基本方法,不同的子類可以提供基本方法的不同實現,更換和增加新的子類很方便,符合單一職責原則和開閉原則。

缺點

需要爲每一個基本方法的不同實現提供一個子類,如果父類中可變的基本方法太多,將會導致類的個數增加,系統更加龐大,設計也更加抽象,此時,可結合橋接模式來進行設計。

發佈了44 篇原創文章 · 獲贊 54 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章