摘要:責(zé)任鏈模式的具體運(yùn)用以及原理請(qǐng)參見(jiàn)筆者責(zé)任鏈模式改進(jìn)方式引入適配器模式關(guān)于接口適配器模式原理以及使用場(chǎng)景請(qǐng)參見(jiàn)筆者適配器模式。
1 責(zé)任鏈模式現(xiàn)存缺點(diǎn)
由于責(zé)任鏈大多數(shù)都是不純的情況,本案例中,只要校驗(yàn)失敗就直接返回,不繼續(xù)處理接下去責(zé)任鏈中的其他校驗(yàn)邏輯了,故而出現(xiàn)如果某個(gè)部分邏輯是要由多個(gè)校驗(yàn)器組成一個(gè)整理的校驗(yàn)邏輯的話,則此責(zé)任鏈模式則顯現(xiàn)出了它的不足之處了。(責(zé)任鏈模式的具體運(yùn)用以及原理請(qǐng)參見(jiàn)筆者github wiki 2 責(zé)任鏈模式)
2 改進(jìn)方式 2.1 引入適配器模式關(guān)于接口適配器模式原理以及使用場(chǎng)景請(qǐng)參見(jiàn)筆者github wiki 12 適配器模式 。
2.2 引入接口默認(rèn)方法事例代碼請(qǐng)參見(jiàn)工程 design-patterns-business中的 defaultmethod包下的代碼。2.2.1 概念
java8引入了一個(gè) default medthod
使用 default 關(guān)鍵字
Spring 4.2支持加載在默認(rèn)方法里聲明的bean
2.2.2 優(yōu)點(diǎn)
用來(lái)擴(kuò)展已有的接口,在對(duì)已有接口的使用不產(chǎn)生任何影響的情況下,添加擴(kuò)展。
比如我們已經(jīng)投入使用的接口需要拓展一個(gè)新的方法,在Java8以前,如果為一個(gè)使用的接口增加一個(gè)新方法,則我們必須在所有實(shí)現(xiàn)類中添加該方法的實(shí)現(xiàn),否則編譯會(huì)出現(xiàn)異常。如果實(shí)現(xiàn)類數(shù)量少并且我們有權(quán)限修改,可能會(huì)工作量相對(duì)較少。如果實(shí)現(xiàn)類比較多或者我們沒(méi)有權(quán)限修改實(shí)現(xiàn)類源代碼,這樣可能就比較麻煩。而默認(rèn)方法則解決了這個(gè)問(wèn)題,它提供了一個(gè)實(shí)現(xiàn),當(dāng)沒(méi)有顯示提供其他實(shí)現(xiàn)時(shí)就采用這個(gè)實(shí)現(xiàn),這樣新添加的方法將不會(huì)破壞現(xiàn)有代碼。
默認(rèn)方法的另一個(gè)優(yōu)勢(shì)是該方法是可選的,子類可以根據(jù)不同的需求Override默認(rèn)實(shí)現(xiàn)。
例如,我們定義一個(gè)集合接口,其中有增、刪、改等操作。如果我們的實(shí)現(xiàn)類90%都是以數(shù)組保存數(shù)據(jù),那么我們可以定義針對(duì)這些方法給出默認(rèn)實(shí)現(xiàn),而對(duì)于其他非數(shù)組集合或者有其他類似業(yè)務(wù),可以選擇性復(fù)寫(xiě)接口中默認(rèn)方法。2.2.3 使用原則
”類優(yōu)先” 原則
若一個(gè)接口中定義了一個(gè)默認(rèn)方法,而另外一個(gè)父類或接口中又定義了一個(gè)同名的方法時(shí)選擇父類中的方法:如果一個(gè)父類提供了具體的實(shí)現(xiàn),那么接口中具有相同名稱和參數(shù)的默認(rèn)方法會(huì)被忽略。
接口沖突原則
如果一個(gè)父接口提供一個(gè)默認(rèn)方法,而另一個(gè)接口也提供了一個(gè)具有相同名稱和參數(shù)列表的方法(不管方法是否是默認(rèn)方法),那么必須覆蓋該方法來(lái)解決沖突。
4.3 項(xiàng)目演示 3.1 邏輯梳理由于系統(tǒng)業(yè)務(wù)需求的變更,目前有兩種業(yè)務(wù)需求,
一種就是按照原來(lái)的文件上傳需求,上傳過(guò)程中需要校驗(yàn)操作,只要校驗(yàn)失敗了,就直接返回失敗信息,整個(gè)文件都不需要做處理了。(參見(jiàn)之前的文章 Java設(shè)計(jì)模式綜合運(yùn)用(門(mén)面+模版方法+責(zé)任鏈+策略))
另一種就是校驗(yàn)的邏輯都一樣,但是如果校驗(yàn)失敗的情況,需要繼續(xù)往下執(zhí)行,記錄一下失敗id即可,即需要把失敗的記錄也保存到數(shù)據(jù)庫(kù)中,比如約束字段不符合約束規(guī)則的情況,則把此字段置空,然后繼續(xù)執(zhí)行其他校驗(yàn)器邏輯即可。(本節(jié)需要討論的問(wèn)題)
3.2 實(shí)現(xiàn)方案根據(jù)第2節(jié)的改進(jìn)方式可以知道,我們有兩種方式改進(jìn)以上邏輯。
3.2.1 采用適配器模式代碼參見(jiàn)2.1版本的,地址為:https://github.com/landy8530/...
若采用適配器模式,此處我們會(huì)采用接口適配器模式。
接口需要多增加一個(gè)不用鏈?zhǔn)秸{(diào)用的校驗(yàn)方法,定義如下,
/** * 業(yè)務(wù)校驗(yàn)統(tǒng)一接口,增加了接口的默認(rèn)方法實(shí)現(xiàn),這樣可以更加方便且自由選擇實(shí)現(xiàn)接口的哪些方法。 * @author landyl * @create 10:32 AM 05/09/2018 * @version 2.0 * @since 1.0 */ public interface Validator{ /** * 需要引入責(zé)任鏈的時(shí)候,則采用此方法 * @param detail * @param chain * @return * @throws BusinessValidationException */ String doValidate(R detail, F file, ValidatorChain chain) throws BusinessValidationException; /** * 不需要責(zé)任鏈的時(shí)候,則可以直接調(diào)用此方法的實(shí)現(xiàn)即可 * @param detail * @return * @throws BusinessValidationException */ boolean doValidate(R detail, F file) throws BusinessValidationException; }
適配器類定義如下抽象類,
/** * @author: landy * @date: 2019/5/19 14:48 * @description: */ public abstract class ValidatorAdapter implements Validator{ public String doValidate(RequestDetail detail, RequestFile file, ValidatorChain chain) throws BusinessValidationException { boolean isValid = this.doValidate(detail, file); //校驗(yàn)失敗了,就直接返回 if(!isValid) { if(detail.getValidationResult() != null) { return detail.getValidationResult().getResultMsg(); } } //否則往責(zé)任鏈中下一個(gè)校驗(yàn)器進(jìn)行處理 return chain.doValidate(detail, file); } @Override public boolean doValidate(RequestDetail detail, RequestFile file) throws BusinessValidationException { return true; } }
如此一來(lái),因?yàn)樵黾恿嗽薪涌谥械姆椒?,則需要在每個(gè)實(shí)現(xiàn)類中都增加一個(gè)實(shí)現(xiàn)方法,雖然有以上的適配器類,但是也要把之前實(shí)現(xiàn)接口改為繼承該適配器類,即 ValidatorAdapter 類。比如,
/** * @author landyl * @create 2:57 PM 05/09/2018 */ @Component(ValidatorConstants.BEAN_NAME_CUSTOMER_IS_ACTIVE) public class IsActiveValidator extends ValidatorAdapter { @Override public boolean doValidate(RequestDetail detail, RequestFile file) throws BusinessValidationException { if (StringUtils.isNotEmpty(detail.getIsActive()) && !"0".equals(detail.getIsActive()) && !"1".equals(detail.getIsActive())) { String result = "An invalid Is Active setting was provided. Accepted Value(s): 0, 1 (0 = No; 1 = Yes)."; detail.bindValidationResult(Constants.INVALID_IS_ACTIVE,result); return false; } return true; } }
而且,因?yàn)檫m配器類中的參數(shù)都是RequestDetail 和 RequestFile類,故而子類可能需要做強(qiáng)制轉(zhuǎn)換操作,如:
@Component(ValidatorConstants.BEAN_NAME_CUSTOMER_BUSINESS_LINE) public class BusinessLineValidator extends ValidatorAdapter { public String doValidate(RequestDetail detail, RequestFile file, ValidatorChain chain) throws BusinessValidationException { if(detail instanceof CustomerRequestDetail) { CustomerRequestDetail crd = (CustomerRequestDetail)detail; String result = validateBusinessLineLogic(crd); if(!Constants.VALID.equals(result)){ return result; } } return chain.doValidate(detail,file); } ... }
局限性比較多。
3.2.2 采用接口默認(rèn)方法代碼參見(jiàn)2.0版本,地址為:https://github.com/landy8530/... ,后續(xù)也會(huì)merge到master版本。
接口定義如下,采用默認(rèn)方法實(shí)現(xiàn),
/** * 業(yè)務(wù)校驗(yàn)統(tǒng)一接口,增加了接口的默認(rèn)方法實(shí)現(xiàn),這樣可以更加方便且自由選擇實(shí)現(xiàn)接口的哪些方法。 * @author landyl * @create 10:32 AM 05/09/2018 * @version 2.0 * @since 1.0 */ public interface Validator{ /** * 需要引入責(zé)任鏈的時(shí)候,則采用此方法 * @param detail * @param chain * @return * @throws BusinessValidationException */ default String doValidate(R detail, F file, ValidatorChain chain) throws BusinessValidationException { boolean isValid = this.doValidate(detail, file); //校驗(yàn)失敗了,就直接返回 if(!isValid) { if(detail.getValidationResult() != null) { return detail.getValidationResult().getResultMsg(); } } //否則往責(zé)任鏈中下一個(gè)校驗(yàn)器進(jìn)行處理 return chain.doValidate(detail, file); } /** * 不需要責(zé)任鏈的時(shí)候,則可以直接調(diào)用此方法的實(shí)現(xiàn)即可 * @param detail * @return * @throws BusinessValidationException */ default boolean doValidate(R detail, F file) throws BusinessValidationException { return true; } }
由此可見(jiàn),其實(shí)此處的默認(rèn)方法,就是上節(jié)中的適配器類的實(shí)現(xiàn)方式而已,而且對(duì)于后續(xù)的實(shí)現(xiàn)類而言,無(wú)須變動(dòng)(針對(duì)不需要改動(dòng)業(yè)務(wù)邏輯的類而言)。即使針對(duì)需要變動(dòng)業(yè)務(wù)邏輯的類,也只是改動(dòng)部分而已,而且不需要類型強(qiáng)制轉(zhuǎn)換操作。避免了不必要的異常出現(xiàn)。
3.2.3 方案對(duì)比經(jīng)過(guò)以上代碼實(shí)現(xiàn)可以發(fā)現(xiàn),默認(rèn)接口實(shí)現(xiàn)有其非常明顯的優(yōu)勢(shì),即擁抱變化,擴(kuò)展已有接口,向后兼容。
3.3 邏輯測(cè)試具體的測(cè)試代碼可以參見(jiàn)相應(yīng)版本的github單元測(cè)試代碼即可。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74578.html
摘要:注解方式優(yōu)點(diǎn)使用注解方式可以極大的減少使用模版方法模式帶來(lái)的擴(kuò)展時(shí)需要繼承模版類的弊端,工廠注解的方式可以無(wú)需關(guān)心其他業(yè)務(wù)類的實(shí)現(xiàn),而且減少了類膨脹的風(fēng)險(xiǎn)。 在上一篇文章Java設(shè)計(jì)模式綜合運(yùn)用(門(mén)面+模版方法+責(zé)任鏈+策略)中,筆者寫(xiě)了一篇門(mén)面模式、模版方法、責(zé)任鏈跟策略模式的綜合運(yùn)用的事例文章,但是后來(lái)筆者發(fā)現(xiàn),在實(shí)現(xiàn)策略模式的實(shí)現(xiàn)上,發(fā)現(xiàn)了一個(gè)弊端:那就是如果在后續(xù)業(yè)務(wù)發(fā)展中,需...
摘要:此案例中,門(mén)面類為,然后各個(gè)門(mén)面方法的參數(shù)均為抽象類,通過(guò)決定調(diào)用中的哪個(gè)子類。抽象類持有類的對(duì)象,并且實(shí)現(xiàn)累的一個(gè)接口是為了容器啟動(dòng)完成的時(shí)候自動(dòng)把相應(yīng)的校驗(yàn)器加入到校驗(yàn)器鏈中。 引言:很久沒(méi)有更新了,主要是工作忙。最近,工作中一個(gè)子系統(tǒng)升級(jí),把之前不易擴(kuò)展的缺點(diǎn)給改進(jìn)了一下,主要是運(yùn)用了幾個(gè)設(shè)計(jì)模式進(jìn)行稍微改造了一下。本文也同步發(fā)布至簡(jiǎn)書(shū),地址: https://www.jians...
摘要:在新智能合約的構(gòu)造函數(shù)中,將引用我們的合約工廠的地址。以太坊,主要是針對(duì)工程師使用進(jìn)行區(qū)塊鏈以太坊開(kāi)發(fā)的詳解。以太坊入門(mén)教程,主要介紹智能合約與應(yīng)用開(kāi)發(fā),適合入門(mén)。這里是原文用工廠模式管理多個(gè)智能合約 我們寫(xiě)了一份小的計(jì)算合約作為Hello World。如果我們可以創(chuàng)建一個(gè)允許用戶創(chuàng)建自己的計(jì)數(shù)器的合約怎么辦? showImg(https://segmentfault.com/img/...
摘要:面試官要不你來(lái)手寫(xiě)下單例模式唄候選者單例模式一般會(huì)有好幾種寫(xiě)法候選者餓漢式簡(jiǎn)單懶漢式在方法聲明時(shí)加鎖雙重檢驗(yàn)加鎖進(jìn)階懶漢式靜態(tài)內(nèi)部類優(yōu)雅懶漢式枚舉候選者所謂餓漢式指的就是還沒(méi)被用到,就直接初始化了對(duì)象。面試官:我看你的簡(jiǎn)歷寫(xiě)著熟悉常見(jiàn)的設(shè)計(jì)模式,要不你來(lái)簡(jiǎn)單聊聊你熟悉哪幾個(gè)吧?候選者:常見(jiàn)的工廠模式、代理模式、模板方法模式、責(zé)任鏈模式、單例模式、包裝設(shè)計(jì)模式、策略模式等都是有所了解的候選者:...
閱讀 3059·2023-04-26 03:01
閱讀 3547·2023-04-25 19:54
閱讀 1600·2021-11-24 09:39
閱讀 1382·2021-11-19 09:40
閱讀 4262·2021-10-14 09:43
閱讀 2099·2019-08-30 15:56
閱讀 1504·2019-08-30 13:52
閱讀 1669·2019-08-29 13:05