設(shè)計(jì)模式詳解——模版方法模式

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

一、模版方法模式的定義

模板方法模式是類的行為模式。準(zhǔn)備一個(gè)抽象類,將部分邏輯以具體方法以及具體構(gòu)造函數(shù)的形式實(shí)現(xiàn),然后聲明一些抽象方法來迫使子類實(shí)現(xiàn)剩余的邏輯。不同的子類可以以不同的方式實(shí)現(xiàn)這些抽象方法,從而對(duì)剩余的邏輯有不同的實(shí)現(xiàn)。這就是模板方法模式的用意。

二、模版方法模式的結(jié)構(gòu)

模板方法模式是所有模式中最為常見的幾個(gè)模式之一,是基于繼承的代碼復(fù)用的基本技術(shù)。

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

模板方法所代表的行為稱為頂級(jí)行為,其邏輯稱為頂級(jí)邏輯。模板方法模式的靜態(tài)結(jié)構(gòu)圖如下所示:


這里涉及到兩個(gè)角色:

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

■  定義了一個(gè)或多個(gè)抽象操作,以便讓子類實(shí)現(xiàn)。這些抽象操作叫做基本操作,它們是一個(gè)頂級(jí)邏輯的組成步驟。

■  定義并實(shí)現(xiàn)了一個(gè)模板方法。這個(gè)模板方法一般是一個(gè)具體方法,它給出了一個(gè)頂級(jí)邏輯的骨架,而邏輯的組成步驟在相應(yīng)的抽象操作中,推遲到子類實(shí)現(xiàn)。頂級(jí)邏輯也有可能調(diào)用一些具體方法。

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

■  實(shí)現(xiàn)父類所定義的一個(gè)或多個(gè)抽象方法,它們是一個(gè)頂級(jí)邏輯的組成步驟。

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

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

public abstract class AbstractTemplate {
    /**
     * 模板方法
     */
    public void templateMethod(){
        //調(diào)用基本方法
        abstractMethod();
        hookMethod();
        concreteMethod();
    }
    /**
     * 基本方法的聲明(由子類實(shí)現(xiàn))
     */
    protected abstract void abstractMethod();
    /**
     * 基本方法(空方法)
     */
    protected void hookMethod(){}
    /**
     * 基本方法(已經(jīng)實(shí)現(xiàn))
     */
    private final void concreteMethod(){
        //業(yè)務(wù)相關(guān)的代碼
    }
}

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

public class ConcreteTemplate extends AbstractTemplate{
    //基本方法的實(shí)現(xiàn)
    @Override
    public void abstractMethod() {
        //業(yè)務(wù)相關(guān)的代碼
    }
    //重寫父類的方法
    @Override
    public void hookMethod() {
        //業(yè)務(wù)相關(guān)的代碼
    }
}

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

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

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

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

模板方法

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

一個(gè)抽象類可以有任意多個(gè)模板方法,而不限于一個(gè)。每一個(gè)模板方法都可以調(diào)用任意多個(gè)具體方法。

基本方法

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

●  抽象方法:一個(gè)抽象方法由抽象類聲明,由具體子類實(shí)現(xiàn)。在Java語言里抽象方法以abstract關(guān)鍵字標(biāo)示。

●  具體方法:一個(gè)具體方法由抽象類聲明并實(shí)現(xiàn),而子類并不實(shí)現(xiàn)或置換。

●  鉤子方法:一個(gè)鉤子方法由抽象類聲明并實(shí)現(xiàn),而子類會(huì)加以擴(kuò)展。通常抽象類給出的實(shí)現(xiàn)是一個(gè)空實(shí)現(xiàn),作為方法的默認(rèn)實(shí)現(xiàn)。

在上面的例子中,AbstractTemplate是一個(gè)抽象類,它帶有三個(gè)方法。其中abstractMethod()是一個(gè)抽象方法,它由抽象類聲明為抽象方法,并由子類實(shí)現(xiàn);hookMethod()是一個(gè)鉤子方法,它由抽象類聲明并提供默認(rèn)實(shí)現(xiàn),并且由子類置換掉。concreteMethod()是一個(gè)具體方法,它由抽象類聲明并實(shí)現(xiàn)。

默認(rèn)鉤子方法

一個(gè)鉤子方法常常由抽象類給出一個(gè)空實(shí)現(xiàn)作為此方法的默認(rèn)實(shí)現(xiàn)。這種空的鉤子方法叫做“Do Nothing Hook”。具體模版類中可以選擇是否重寫鉤子方法,通常重寫鉤子方法是為了對(duì)模版方法中的步驟進(jìn)行控制,判斷鉤子方法中的狀態(tài),是否進(jìn)行下一步操作。

四、模版方法的具體實(shí)例

考慮一個(gè)計(jì)算存款利息的例子。假設(shè)系統(tǒng)需要支持兩種存款賬號(hào),即貨幣市場(chǎng)(Money Market)賬號(hào)和定期存款(Certificate of Deposite)賬號(hào)。這兩種賬號(hào)的存款利息是不同的,因此,在計(jì)算一個(gè)存戶的存款利息額時(shí),必須區(qū)分兩種不同的賬號(hào)類型。

這個(gè)系統(tǒng)的總行為應(yīng)當(dāng)是計(jì)算出利息,這也就決定了作為一個(gè)模板方法模式的頂級(jí)邏輯應(yīng)當(dāng)是利息計(jì)算。由于利息計(jì)算涉及到兩個(gè)步驟:一個(gè)基本方法給出賬號(hào)種類,另一個(gè)基本方法給出利息百分比。這兩個(gè)基本方法構(gòu)成具體邏輯,因?yàn)橘~號(hào)的類型不同,所以具體邏輯會(huì)有所不同。

顯然,系統(tǒng)需要一個(gè)抽象角色給出頂級(jí)行為的實(shí)現(xiàn),而將兩個(gè)作為細(xì)節(jié)步驟的基本方法留給具體子類實(shí)現(xiàn)。由于需要考慮的賬號(hào)有兩種:一是貨幣市場(chǎng)賬號(hào),二是定期存款賬號(hào)。系統(tǒng)的類結(jié)構(gòu)如下圖所示。


抽象模板角色類

public abstract class Account {
    /**
     * 模板方法,計(jì)算利息數(shù)額
     * @return    返回利息數(shù)額
     */
    public final double calculateInterest(){
        double interestRate = doCalculateInterestRate();
        String accountType = doCalculateAccountType();
        double amount = calculateAmount(accountType);
        return amount * interestRate;
    }
    /**
     * 基本方法留給子類實(shí)現(xiàn)
     */
    protected abstract String doCalculateAccountType();
    /**
     * 基本方法留給子類實(shí)現(xiàn)
     */
    protected abstract double doCalculateInterestRate();
    /**
     * 基本方法,已經(jīng)實(shí)現(xiàn)
     */
    private double calculateAmount(String accountType){
        /**
         * 省略相關(guān)的業(yè)務(wù)邏輯
         */
        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("貨幣市場(chǎng)賬號(hào)的利息數(shù)額為:" + account.calculateInterest());
        account = new CDAccount();
        System.out.println("定期賬號(hào)的利息數(shù)額為:" + account.calculateInterest());
    }

}

五、模板方法模式效果與適用場(chǎng)景

模板方法模式是基于繼承的代碼復(fù)用技術(shù),它體現(xiàn)了面向?qū)ο蟮闹T多重要思想,是一種使用較為頻繁的模式。模板方法模式廣泛應(yīng)用于框架設(shè)計(jì)中,以確保通過父類來控制處理流程的邏輯順序(如框架的初始化,測(cè)試流程的設(shè)置等)。

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

(1) 對(duì)一些復(fù)雜的算法進(jìn)行分割,將其算法中固定不變的部分設(shè)計(jì)為模板方法和父類具體方法,而一些可以改變的細(xì)節(jié)由其子類來實(shí)現(xiàn)。即:一次性實(shí)現(xiàn)一個(gè)算法的不變部分,并將可變的行為留給子類來實(shí)現(xiàn)。

(2) 各子類中公共的行為應(yīng)被提取出來并集中到一個(gè)公共父類中以避免代碼重復(fù)。

(3) 需要通過子類來決定父類算法中某個(gè)步驟是否執(zhí)行,實(shí)現(xiàn)子類對(duì)父類的反向控制。

六、模版方法模式的優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

(1) 在父類中形式化地定義一個(gè)算法,而由它的子類來實(shí)現(xiàn)細(xì)節(jié)的處理,在子類實(shí)現(xiàn)詳細(xì)的處理算法時(shí)并不會(huì)改變算法中步驟的執(zhí)行次序。

(2) 模板方法模式是一種代碼復(fù)用技術(shù),它在類庫設(shè)計(jì)中尤為重要,它提取了類庫中的公共行為,將公共行為放在父類中,而通過其子類來實(shí)現(xiàn)不同的行為,它鼓勵(lì)我們恰當(dāng)使用繼承來實(shí)現(xiàn)代碼復(fù)用。

(3) 可實(shí)現(xiàn)一種反向控制結(jié)構(gòu),通過子類覆蓋父類的鉤子方法來決定某一特定步驟是否需要執(zhí)行。

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

缺點(diǎn)

需要為每一個(gè)基本方法的不同實(shí)現(xiàn)提供一個(gè)子類,如果父類中可變的基本方法太多,將會(huì)導(dǎo)致類的個(gè)數(shù)增加,系統(tǒng)更加龐大,設(shè)計(jì)也更加抽象,此時(shí),可結(jié)合橋接模式來進(jìn)行設(shè)計(jì)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容