CRUD很無聊?一塊兒學設計模式吧!--模板模式

若是在項目開發中你常常看到一個類的某些方法和其餘類的方法功能相同,只有部分不一樣或者只有具體實現不一樣,亦或者是你看到某些方法在多個地方都存在,有不少重複代碼,這個時候你就能夠拿出模板設計模式了。java

定義與特色

模板方法(Template Method)模式的定義以下: 定義一個操做中的算法骨架,而將算法的一些步驟延遲到子類中,使得子類能夠不改變該算法結構的狀況下重定義該算法的某些特定步驟。它是一種類行爲型模式。面試

模板模式的主要優勢以下:算法

  • 它封裝了不變部分,擴展可變部分。它把認爲是不變部分的算法封裝到父類中實現,而把可變部分算法由子類繼承實現,便於子類繼續擴展。
  • 它在父類中提取了公共的部分代碼,便於代碼複用。
  • 部分方法是由子類實現的,所以子類能夠經過擴展方式增長相應的功能,符合開閉原則。

主要缺點以下:設計模式

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

UML

image

角色定義

模板模式涉及三個角色:框架

  • 抽象類(AbstractClass)角色:定義一個操做的算法輪廓和框架。它由一個模板方法和若干個基本方法組成。 模板方法(templateMethod): 定義了算法的骨架,按某種順序調用其包含的基本方法,使用public修飾; 基本方法:是整個算法中的一個步驟,使用protected修飾,包含如下幾個類型:
    • 抽象方法:在抽象類中申明,由具體子類實現
    • 具體方法:在抽象類中實現,可是子類能夠繼承或重寫它。
    • 鉤子方法:在抽象類中已經實現,包括用於判斷的邏輯方法和須要子類重寫的空方法兩種。

場景實戰

咱們的報銷系統分爲平常費用報銷和差旅費用報銷,報銷的流程是先根據報銷單上帶的費用計算出報銷金額,而後計算出報銷單中的補貼金額(如果差旅類型報銷才須要計算補貼,平常報銷不須要計算補貼),最後調用第三方接口建立流程。這個場景就適合用模板設計模式實現。ide

抽象類定義

定義報銷流程的算法框架,算法框架使用final修飾,對於必需要子類實現的方法用abstract關鍵字修飾。ui

public abstract class AbstractReimburse {
    /**
     * 用做算法的模板
     * 定義成final,以避免子模板改變算法的順序
     */
    final void calAndCreateFlow(){
        BigDecimal totalMoney;
        BigDecimal changeMoney = calChangeMoney();
        BigDecimal subsidyMoney = BigDecimal.ZERO;
        if(hasTravel()){
            subsidyMoney = calSubsidyMoney();
        }
        totalMoney = changeMoney.add(subsidyMoney);
        createWorkeFlow(totalMoney);
    }

    /**
     * 具體方法,子類判斷是否須要實現
     * @param totalMoney 報銷總金額
     */
    protected void createWorkeFlow(BigDecimal totalMoney) {
        System.out.println("開始建立流程...,總報銷金額:"+totalMoney);
        //todo
    }

    /**
     * 鉤子方法,由子類決定是否實現,鉤子能夠做爲條件控制,影響抽象類中的算法流程
     * 判斷是否須要計算補貼
     */
    boolean hasTravel() {
        return false;
    }

    /**
     * 抽象方法,須要子類去實現
     * 返回須要報銷的費用金額
     * @return 報銷的費用總金額
     */
    abstract BigDecimal calChangeMoney();

    /**
     * 返回須要報銷的補貼金額
     */
    abstract BigDecimal calSubsidyMoney();
}

具體實現類

  • 差旅類報銷實現邏輯
/**
 * 差旅類報銷實現邏輯
 */
public class TravelReimburse extends AbstractReimburse{

    @Override
    BigDecimal calChangeMoney() {
        System.out.println("差旅類報銷計算費用金額");
        return new BigDecimal(1000);
    }

    @Override
    BigDecimal calSubsidyMoney() {
        System.out.println("差旅類報銷須要計算補貼");
        return new BigDecimal(500);
    }

    @Override
    boolean hasTravel(){
        return true;
    }
}
  • 平常類報銷實現邏輯
/**
 * 平常類報銷實現邏輯
 */
public class DailyReimburse extends AbstractReimburse{

    @Override
    BigDecimal calChangeMoney() {
        System.out.println("平常類報銷計算費用金額");
        return new BigDecimal(100);
    }

    @Override
    BigDecimal calSubsidyMoney() {
        return BigDecimal.ZERO;
    }

}

客戶端模擬業務流程

public class ReimburseClient {
    public static void main(String[] args) {
        //差旅類報銷處理邏輯
        TravelReimburse travelReimburse = new TravelReimburse();
        travelReimburse.calAndCreateFlow();
        System.out.println("===========================");
        //平常類報銷處理邏輯
        DailyReimburse dailyReimburse = new DailyReimburse();
        dailyReimburse.calAndCreateFlow();

    }
}

運行結果

image

應用場景

模板模式應該是衆多設計模式中相對簡單的一種,可是它使用的頻率可一點也不低,在各類開源框架代碼中均可以看到它的身影,模板設計模式的應用場景主要有如下幾類:設計

  • 在多個子類中擁有相同的方法,並且邏輯相同,能夠將這些方法抽出來放到一個模板抽象類中
  • 程序主框架相同,僅實現細節不一樣時,也可使用模板方法

tips 記得幾年前電話面試的時候,面試官問我有沒有用過模板設計模式,我回答說「啊,模板?你說的是freemarker嗎?巴拉巴拉一大堆」,而後電話嘟嘟嘟,留我一人在風中凌亂。3d

相關文章

歡迎關注個人我的公衆號:JAVA日知錄

相關文章
相關標籤/搜索