1. 程式人生 > >Java設計模式學習記錄-模板方法模式

Java設計模式學習記錄-模板方法模式

前言

模板方法模式,定義一個操作中演算法的骨架,而將一些步驟延遲到子類中。使得子類可以不改變一個演算法的結構即可重新定義該演算法的某些特定步驟。

模板方法模式

概念介紹

 模板方法模式,其實是很好理解的,具體理解為,定義一個操作中演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個演算法結構即可衝定義該演算法的某些特定步驟。模板方法模式是最為常見的設計模式之一,是基於繼承的程式碼複用技術。架構師定義一套骨架,開發工程師按照骨架去實現具體的邏輯。

舉例

在具體的專案中其實使用模板方法的場景有很多,就舉我之間遇到過的一個場景吧,這是一個實際的專案,結合這個專案的中的場景可以引出模板方法模式的使用方法,

在一個審批流程的專案,會有很多種的申請單,例如:出差申請單、請假申請單、採購申請單、付款申請單等等。每個申請單在提交的時候都要先校驗申請單的引數是否正確,以及在提交成功後去通知審批人,有待審批的單子了。所以說,這個過程都是一樣的,只不過是,每個申請單的校驗引數以及通知審批人的方法會有所不同。那麼這樣就可以把這個提交的過程定義成一個模板,然後每個審批單的都按照這個流程來進行提交申請就可以了。

具體程式碼實現如下:

流程模板類

/**
 * 流程模板類
 */
public abstract class BaseProcess {

    /**
     * 提交流程
     
*/ public void submitProcess(Map<String,String> paramMap){ boolean checkResult = checkParameter(paramMap); if(checkResult){ System.out.println("提交流程成功!"); remindApprovers(); }else { System.out.println("提交流程失敗!"); } }
/** * 校驗引數 * @return */ public abstract boolean checkParameter(Map<String,String> paramMap); /** * 提醒審批人 */ public abstract void remindApprovers(); }

差旅審批流程

/**
 * 差旅審批流程
 *
 */
public class BusinessTravelProcess extends BaseProcess{
    /**
     * 校驗引數
     *
     * @return
     */
    @Override
    public boolean checkParameter(Map<String,String> paramMap) {

        if(null!=paramMap.get("result")&&paramMap.get("result").equals("true")){
            System.out.println("差旅審批單引數校驗成功!");
            return true;
        }else {
            System.out.println("差旅審批單引數校驗失敗!");
            return false;
        }
    }

    /**
     * 提醒審批人
     */
    @Override
    public void remindApprovers() {
        System.out.println("有新的差旅申請提交了。");
    }
}

請假審批流程

/**
 * 請假審批流程
 */
public class LeaveApplyProcess extends BaseProcess {
    /**
     * 校驗引數
     *
     * @return
     */
    @Override
    public boolean checkParameter(Map<String,String> paramMap) {

        if(null!=paramMap.get("result")&&paramMap.get("result").equals("true")){
            System.out.println("請假審批單引數校驗成功!");
            return true;
        }else {
            System.out.println("請假審批單引數校驗成功!");
            return false;
        }

    }

    /**
     * 提醒審批人
     */
    @Override
    public void remindApprovers() {
        System.out.println("有新的請假申請提交了。");
    }
}

測試類

public class Client {

    public static void main(String[] args) {

        //建立請假申請單
        BaseProcess leaveApply = new LeaveApplyProcess();
        Map<String,String> paramMap = Maps.newHashMap();
        paramMap.put("result","true");
        //提交採購申請單
        leaveApply.submitProcess(paramMap);
        //建立差旅申請單
        BaseProcess business = new BusinessTravelProcess();
        paramMap = Maps.newHashMap();
        paramMap.put("result","false");
        //提交差旅申請單
        business.submitProcess(paramMap);

    }

}

執行結果

請假審批單引數校驗成功!
提交流程成功!
有新的請假申請提交了。
差旅審批單引數校驗失敗!
提交流程失敗!

上面的這個例子就是使用的模板方法模式,這個場景是一些業務功能,大體框架是固定的,只是一些具體的實現細節可能不同。用模板方法能提高程式碼的複用性和系統的靈活性。

結構分析

下面來分析一下模板方法的具體結構組成,如下是模板方法模式的類圖。

模板方法模式中,具體就兩個角色。

1、AbstractClass(抽象類):在抽象類中定義了一系列基本操作(PrimitiveOperations),這些基本操作可以是具體的,也可以是抽象的,每一個基本操作對應演算法的一個步驟,在其子類中可以重定義或實現這些步驟。

2、ConcreteClass(具體子類):它是抽象類的子類,用於實現在父類中宣告的抽象基本操作以完成子類特定演算法的步驟,也可以覆蓋在父類中已經實現的具體基本操作。

但是模板方法模式的實現是離不開這三個方法的

1、基本方法

基本方法也稱為基本操作,是由子類實現的方法,並且在模板方法中被呼叫。

2、模板方法

模板方法可以有一個或幾個,一般是一個具體的方法,也就是一個骨架,實現對基本方法的排程,完成固定的邏輯。為了防止惡意的操作,一般模板方法都加上final關鍵字,不允許被覆寫。

3、鉤子方法

鉤子方法由抽象類宣告並加以實現。但是子類可以去擴充套件,子類可以通過擴充套件鉤子方法,來影響模板方法的邏輯。抽象類的任務是搭建邏輯的框架,通常由經驗豐富的人員編寫,因為抽象類的好壞直接決定了程式是否穩定。

總結

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

主要優點

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

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

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

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

主要缺點

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

2、由於每個子類的方法會影響到了父類,這裡違反了里氏替換原則,會給程式帶來風險。

適用場景

1、對一些複雜的演算法進行分割,將其演算法中固定不變的部分設計為模板方法和父類具體方法,而一些可以改變的細節由其子類來實現。 

2、各子類中公共的行為應被提取出來並集中到一個公共父類中以避免程式碼重複。

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