原始碼中的設計模式--模板方法模式(鉤子方法)

北漂程式設計師發表於2022-05-15

  在上次《原始碼中的設計模式--模板方法模式》中分享了有關模板方法設計模式方面的東西,不知道還有印象沒,重溫下其釋義,

模板方法模式在一個方法中定義一個演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變演算法結構的情況下,重寫定義演算法中的某些步驟。

  在上次中舉了這樣的場景,要呼叫系統A、系統B介面,把兩個系統的資料讀取過來,儲存在我們自己的資料庫中,其實現的UML圖如下,

  現在突然,系統A的對接人說,在呼叫他的介面前需要進行校驗,驗證身份,才可以進行呼叫。看看上面的UML圖我們要怎麼修改程式碼,我們把之前的場景抽象了四步:組裝引數、傳送請求、處理返回引數、儲存資料庫,現在系統A需要校驗身份,校驗這個過程是系統A獨有的嗎,顯然不是,原則上呼叫任何一個系統的介面都需要驗證許可權,只有許可權通過了才可以呼叫,那麼校驗這個肯定是上述場景中的一步,為此上面的場景抽象為五步:組裝引數、校驗許可權、傳送請求、處理返回引數、儲存資料。而且校驗許可權這個肯定每個系統的驗證方式是不一樣的,所以需要每個實現類定義自己的實現,也就是它必須是一個抽象的方法

  現在還有一個實際的問題,系統A需要校驗許可權,系統B不需要,兩個實現類均實現了校驗許可權的方法,豈不是都會進行校驗,可不可以判斷下是否需要校驗,而且這個判斷最好交給實現類來實現,由實現類決定是否需要校驗。為此上面的UML變成了下面的樣子,

  上面的UM類圖有什麼玄機嗎,聰明的你肯定看出來了,我再絮叨絮叨。在AbastractSyncData抽象類中增加了抽象方法checkAuthority()和非抽象方法isCheckAuthority(),在SyncSystemAImpl類中實現了checkAuthority()方法和isCheckAuthority()方法,而SyncSystemBImpl僅實現了checkAuthority()方法,怎麼樣和我說的一樣吧。

二、最新實現

下面看下現在的每個類,SyncData介面無變化,這裡不再貼出,

AbstractSyncData.java

package com.example.template;

import java.util.Map;

public abstract class AbstractSyncData implements SyncData {
    //定義好同步資料的步驟
    @Override
    public void syncData() {
        //1、組裝引數
        Map param = assembleParam();
        //新增步驟:判斷是否需要校驗許可權
        if (isCheckAuthority()) {
            checkAuthority();
        }
        //2、傳送請求
        String result = sendRequest(param);
        //3、解析
        String result2 = parse(result);
        //4、儲存資料
        saveData(result2);
    }

    //校驗許可權
    protected abstract void checkAuthority();

    //是否校驗許可權,由該方法決定是否呼叫checkAuthority()方法,預設為false不校驗
    protected boolean isCheckAuthority() {
        return false;
    }

    //1、組裝引數,供子類實現自己的邏輯
    protected abstract Map assembleParam();

    //2、傳送請求
    private String sendRequest(Map map) {
        //實際傳送請求,並把資料返回
        System.out.println("傳送請求");
        return "";
    }

    //3、解析返回結果,供子類實現自己的邏輯
    protected abstract String parse(String result);

    //4、儲存資料
    private void saveData(String result) {
        System.out.println("儲存資料");
    }
}

SyncSystemAImpl.java

package com.example.template;

import java.util.HashMap;
import java.util.Map;

public class SyncSystemAImpl extends AbstractSyncData {
    @Override
    protected void checkAuthority() {
        System.out.println("校驗系統A的許可權");
    }

    @Override
    protected Map assembleParam() {
        System.out.println("組裝傳送到系統A的引數");
        return new HashMap();
    }

    @Override
    protected String parse(String result) {
        System.out.println("解析系統A的返回結果");
        return "";
    }

    /**
     * 重寫父類的方法
     *
     * @return
     */
    @Override
    protected boolean isCheckAuthority() {
        return true;
    }
}

SyncSystemBImpl.java

package com.example.template;

import java.util.HashMap;
import java.util.Map;

public class SyncSystemBImpl extends AbstractSyncData {
    @Override
    protected void checkAuthority() {
        System.out.println("校驗系統B的許可權");
    }

    @Override
    protected Map assembleParam() {
        System.out.println("組裝傳送到系統B的引數");
        return new HashMap();
    }

    @Override
    protected String parse(String result) {
        System.out.println("解析系統B的返回結果");
        return "";
    }
}

看下測試結果,

組裝傳送到系統A的引數
校驗系統A的許可權
傳送請求
解析系統A的返回結果
儲存資料
-----------
組裝傳送到系統B的引數
傳送請求
解析系統B的返回結果
儲存資料

Process finished with exit code 0

  從上面的結果可以看到在讀取系統A的介面時多了“校驗系統A的許可權”,而系統B卻沒有,滿足上面的要求。說了那麼多多總算要給今天的主角正名,isCheckAuthority()方法我們稱之為鉤子方法。isCheckAuthority()方法在父類(抽象類)中宣告,且提供了預設的實現,那麼不管子類是否覆蓋該方法都可以,這就是鉤子方法的高明之處。

  在模板方法模式中鉤子方法由抽象類宣告並提供預設實現,該方法不要求子類一定去實現,所以不是抽象方法,子類可以選擇@Override該方法也可以選擇不這麼做,如果不這麼做將會使用父類的邏輯,如果這麼做了則使用子類的邏輯。鉤子方法要在演算法的骨架中有所體現。

  有了鉤子方法可以讓子類有更多的自主處理邏輯的能力,在不改變演算法骨架的前提下提供了更多的便利,使模板方法模式更好用。希望大家在日常的開發中多思考功能場景,儘量對問題進行抽象,從抽象中尋找共性,針對差異化的處理就可以使用“鉤子方法”了。

  今天的分享就到這裡,感謝你能喜歡,下次見。

 

推薦閱讀

《原始碼中的設計模式--模板方法模式》

相關文章