源碼中的設計模式–模板方法模式(鉤子方法)
在上次《源碼中的設計模式–模板方法模式》中分享了有關模板方法設計模式方面的東西,不知道還有印象沒,重溫下其釋義,
模板方法模式在一個方法中定義一個演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變演算法結構的情況下,重寫定義演算法中的某些步驟。
在上次中舉了這樣的場景,要調用系統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該方法也可以選擇不這麼做,如果不這麼做將會使用父類的邏輯,如果這麼做了則使用子類的邏輯。鉤子方法要在演算法的骨架中有所體現。
有了鉤子方法可以讓子類有更多的自主處理邏輯的能力,在不改變演算法骨架的前提下提供了更多的便利,使模板方法模式更好用。希望大家在日常的開發中多思考功能場景,盡量對問題進行抽象,從抽象中尋找共性,針對差異化的處理就可以使用「鉤子方法」了。
今天的分享就到這裡,感謝你能喜歡,下次見。
推薦閱讀