引言:好久沒有更新了,主要是工做忙。最近,工做中一個子系統升級,把以前不易擴展的缺點給改進了一下,主要是運用了幾個設計模式進行稍微改造了一下。
本文也同步發佈至簡書,地址: https://www.jianshu.com/p/962...
在公司的一個實際項目中,須要作一個第三方公司(如下簡稱GMG)的系統集成工做,把該公司的一些訂單數據集成到本身公司平臺下,各個訂單具備一些共性,可是也有其特有的特徵。 通過設計,目前我把訂單分爲POLICY和BOB類型(暫且這麼說吧,反正就是一種訂單類型,你們參照着看就OK)。git
在訂單數據集成到公司平臺前,須要對訂單數據進行一些必要的業務邏輯校驗操做,而且每一個訂單都有本身的校驗邏輯(包含公共的校驗邏輯)。 本節介紹的即是整個訂單集成系統中的校驗邏輯在綜合利用設計模式的基礎上進行架構設計。github
本校驗邏輯主要分爲四個部分:spring
其實上面的RequestValidator的實現邏輯最後都是委託給RequestValidationFacade這個門面類進行相應的校驗操做。設計模式
主要分爲RequestFile和RequestDetail兩個domain,RequestFile接收泛型的類型(即RequestFile), 使得其子類可以自動識別相應的RequestDetail的子類。RequestFile爲抽象類,定義瞭如下抽象方法,由子類實現:緩存
//由子類實現具體的獲取文件明細內容 public abstract List<T> getRequestDetails(); //由子類實現具體的獲取workflow的值 public abstract WorkflowEnum getProcessWorkFlow(); //由子類實現文件列字段名列表 public abstract String[] getDetailHeaders();
RequestDetail及其子類就是workflow對應文件的明細內容。架構
本例中以下規定:dom
以上校驗邏輯在AbstractRequestValidation類相應的子類中實現(validateFileName方法),其實這個枚舉貫穿整個校驗組件,它就是一個針對每一個業務流程定義的一個枚舉策略。函數
在客戶端調用程序中,採用門面模式進行統一的入口(門面模式講究的是脫離具體的業務邏輯代碼)。門面模式封裝的結果就是避免高層模塊深刻子系統內部,同時提供系統的高內聚、低耦合的特性。ui
此案例中,門面類爲RequestValidationFacade,而後各個門面方法的參數均爲抽象類RequestFile,經過RequestFile->getProcessWorkFlow()決定調用AbstractRequestValidation中的哪一個子類。 AbstractRequestValidation類構造方法中定義了以下邏輯:this
requestValidationHandlerMap.put(this.accessWorkflow(),this.accessBeanName());
把子類中Spring自動注入的實體bean緩存到requestValidationHandlerMap中,key即爲WorkflowEnum枚舉值,value爲spring bean name, 而後在門面類中能夠經過對應的枚舉值取得BeanName,進而獲得AbstractRequestValidation相應的子類對象,進行相應的校驗操做。
注:這邊動態調用到AbstractRequestValidation相應的子類對象,其實也是隱藏着【策略模式】的影子。
類圖以下:
在具體的校驗邏輯中,用到核心設計模式即是模版方法模式,AbstractRequestValidation抽象類中定義瞭如下抽象方法:
/** * validate the file details * @param errMsg * @param requestFile * @return */ protected abstract StringBuilder validateFileDetails(StringBuilder errMsg,RequestFile requestFile); /** * validate the file name * @param fileName * @return */ protected abstract String validateFileName(String fileName); /** * return the current CSYNC_UPDATE_WORKFLOW.UPDATE_WORKFLOW_ID * @return */ protected abstract WorkflowEnum accessWorkflow(); /** * return the current file name's format ,such as: csync_policy_yyyyMMdd_HHmmss_count.txt * @return */ protected abstract String accessFileNameFormat(); /** * return the subclass's spring bean name * @return */ protected abstract String accessBeanName();
以上抽象方法就相似咱們常說的鉤子函數,由子類實現便可。類圖以下圖所示:
在AbstractRequestValidation抽象類中有個抽象方法validateFileDetails,校驗的是文件的明細內容中的相應業務規則,此爲核心校驗, 較爲複雜,並且針對每一個業務流程,其校驗邏輯相差較大,在此處,利用了責任鏈模式進行處理。
Validator爲校驗器的父接口,包含兩個泛型參數(即:<R extends RequestDetail,F extends RequestFile>),其實現類能夠方便的轉換須要校驗的文件明細。
String doValidate(R detail, F file, ValidatorChain chain) throws BusinessValidationException;
該方法含有一個ValidatorChain參數,就天然而然的爲該校驗器造成一個鏈條提供便利條件。
ValidatorChain爲校驗器鏈,含有兩個接口方法:
String doValidate(T requestDetail, F requestFile) throws BusinessValidationException; ValidatorChain addValidator(Validator validator, WorkflowEnum workflowId);
該處有一個addValidator方法,爲ValidatorChain對象添加校驗器的方法,返回自己。對應於每一個業務流程須要哪些校驗器就在此實現便可(即AbstractRequestValidation的子類方法validateFileDetails)。
類圖以下圖所示:
若是單單從上面的校驗器實現上來看,若是須要增長一個校驗器,就須要在AbstractRequestValidation的子類方法validateFileDetails中添加,而後進行相應的校驗操做。這樣就會很是的麻煩,沒有作到真正的解耦。 此時,策略模式就發揮到了能夠動態選擇某種校驗策略的做用(Validator的實現類就是一個具體的校驗策略)。
AbstractValidatorHandler抽象類持有FileDetailValidatorChain類的對象,而且實現累Spring的一個接口ApplicationListener(是爲了Spring容器啓動完成的時候自動把相應的校驗器加入到校驗器鏈中)。 核心就是WorkflowEnum這個策略枚舉的做用,在子類能夠動態的取得相應的校驗器對象。
根據子類提供須要的校驗器所在的包名列表和不須要的校驗器列表,動態配置出須要的校驗器鏈表。核心實現邏輯以下:
private void addValidators() { List<Class<? extends Validator>> validators = getValidators(); validators.forEach((validator) -> { String simpleName = validator.getSimpleName(); String beanName = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1); LOGGER.info("Added validator:{},spring bean name is:{}",simpleName,beanName); Validator validatorInstance = ApplicationUtil.getApplicationContext().getBean(beanName,validator); fileDetailValidatorChain.addValidator(validatorInstance,getWorkflowId()); }); }
具體實現能夠參考github代碼便可。
該類含有如下幾個抽象方法:
protected abstract WorkflowEnum getWorkflowId(); /** * the package need to be added the validators * @return */ protected abstract Set<String> getBasePackages(); /** * the classes need to be excluded * @return */ protected abstract Set<Class> excludeClasses();