摘要:責任鏈模式的具體運用以及原理請參見筆者責任鏈模式改進方式引入適配器模式關于接口適配器模式原理以及使用場景請參見筆者適配器模式。
1 責任鏈模式現存缺點
由于責任鏈大多數都是不純的情況,本案例中,只要校驗失敗就直接返回,不繼續處理接下去責任鏈中的其他校驗邏輯了,故而出現如果某個部分邏輯是要由多個校驗器組成一個整理的校驗邏輯的話,則此責任鏈模式則顯現出了它的不足之處了。(責任鏈模式的具體運用以及原理請參見筆者github wiki 2 責任鏈模式)
2 改進方式 2.1 引入適配器模式關于接口適配器模式原理以及使用場景請參見筆者github wiki 12 適配器模式 。
2.2 引入接口默認方法事例代碼請參見工程 design-patterns-business中的 defaultmethod包下的代碼。2.2.1 概念
java8引入了一個 default medthod
使用 default 關鍵字
Spring 4.2支持加載在默認方法里聲明的bean
2.2.2 優點
用來擴展已有的接口,在對已有接口的使用不產生任何影響的情況下,添加擴展。
比如我們已經投入使用的接口需要拓展一個新的方法,在Java8以前,如果為一個使用的接口增加一個新方法,則我們必須在所有實現類中添加該方法的實現,否則編譯會出現異常。如果實現類數量少并且我們有權限修改,可能會工作量相對較少。如果實現類比較多或者我們沒有權限修改實現類源代碼,這樣可能就比較麻煩。而默認方法則解決了這個問題,它提供了一個實現,當沒有顯示提供其他實現時就采用這個實現,這樣新添加的方法將不會破壞現有代碼。
默認方法的另一個優勢是該方法是可選的,子類可以根據不同的需求Override默認實現。
例如,我們定義一個集合接口,其中有增、刪、改等操作。如果我們的實現類90%都是以數組保存數據,那么我們可以定義針對這些方法給出默認實現,而對于其他非數組集合或者有其他類似業務,可以選擇性復寫接口中默認方法。2.2.3 使用原則
”類優先” 原則
若一個接口中定義了一個默認方法,而另外一個父類或接口中又定義了一個同名的方法時選擇父類中的方法:如果一個父類提供了具體的實現,那么接口中具有相同名稱和參數的默認方法會被忽略。
接口沖突原則
如果一個父接口提供一個默認方法,而另一個接口也提供了一個具有相同名稱和參數列表的方法(不管方法是否是默認方法),那么必須覆蓋該方法來解決沖突。
4.3 項目演示 3.1 邏輯梳理由于系統業務需求的變更,目前有兩種業務需求,
一種就是按照原來的文件上傳需求,上傳過程中需要校驗操作,只要校驗失敗了,就直接返回失敗信息,整個文件都不需要做處理了。(參見之前的文章 Java設計模式綜合運用(門面+模版方法+責任鏈+策略))
另一種就是校驗的邏輯都一樣,但是如果校驗失敗的情況,需要繼續往下執行,記錄一下失敗id即可,即需要把失敗的記錄也保存到數據庫中,比如約束字段不符合約束規則的情況,則把此字段置空,然后繼續執行其他校驗器邏輯即可。(本節需要討論的問題)
3.2 實現方案根據第2節的改進方式可以知道,我們有兩種方式改進以上邏輯。
3.2.1 采用適配器模式代碼參見2.1版本的,地址為:https://github.com/landy8530/...
若采用適配器模式,此處我們會采用接口適配器模式。
接口需要多增加一個不用鏈式調用的校驗方法,定義如下,
/** * 業務校驗統一接口,增加了接口的默認方法實現,這樣可以更加方便且自由選擇實現接口的哪些方法。 * @author landyl * @create 10:32 AM 05/09/2018 * @version 2.0 * @since 1.0 */ public interface Validator{ /** * 需要引入責任鏈的時候,則采用此方法 * @param detail * @param chain * @return * @throws BusinessValidationException */ String doValidate(R detail, F file, ValidatorChain chain) throws BusinessValidationException; /** * 不需要責任鏈的時候,則可以直接調用此方法的實現即可 * @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); //校驗失敗了,就直接返回 if(!isValid) { if(detail.getValidationResult() != null) { return detail.getValidationResult().getResultMsg(); } } //否則往責任鏈中下一個校驗器進行處理 return chain.doValidate(detail, file); } @Override public boolean doValidate(RequestDetail detail, RequestFile file) throws BusinessValidationException { return true; } }
如此一來,因為增加了原有接口中的方法,則需要在每個實現類中都增加一個實現方法,雖然有以上的適配器類,但是也要把之前實現接口改為繼承該適配器類,即 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; } }
而且,因為適配器類中的參數都是RequestDetail 和 RequestFile類,故而子類可能需要做強制轉換操作,如:
@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 采用接口默認方法代碼參見2.0版本,地址為:https://github.com/landy8530/... ,后續也會merge到master版本。
接口定義如下,采用默認方法實現,
/** * 業務校驗統一接口,增加了接口的默認方法實現,這樣可以更加方便且自由選擇實現接口的哪些方法。 * @author landyl * @create 10:32 AM 05/09/2018 * @version 2.0 * @since 1.0 */ public interface Validator{ /** * 需要引入責任鏈的時候,則采用此方法 * @param detail * @param chain * @return * @throws BusinessValidationException */ default String doValidate(R detail, F file, ValidatorChain chain) throws BusinessValidationException { boolean isValid = this.doValidate(detail, file); //校驗失敗了,就直接返回 if(!isValid) { if(detail.getValidationResult() != null) { return detail.getValidationResult().getResultMsg(); } } //否則往責任鏈中下一個校驗器進行處理 return chain.doValidate(detail, file); } /** * 不需要責任鏈的時候,則可以直接調用此方法的實現即可 * @param detail * @return * @throws BusinessValidationException */ default boolean doValidate(R detail, F file) throws BusinessValidationException { return true; } }
由此可見,其實此處的默認方法,就是上節中的適配器類的實現方式而已,而且對于后續的實現類而言,無須變動(針對不需要改動業務邏輯的類而言)。即使針對需要變動業務邏輯的類,也只是改動部分而已,而且不需要類型強制轉換操作。避免了不必要的異常出現。
3.2.3 方案對比經過以上代碼實現可以發現,默認接口實現有其非常明顯的優勢,即擁抱變化,擴展已有接口,向后兼容。
3.3 邏輯測試具體的測試代碼可以參見相應版本的github單元測試代碼即可。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/74578.html
摘要:注解方式優點使用注解方式可以極大的減少使用模版方法模式帶來的擴展時需要繼承模版類的弊端,工廠注解的方式可以無需關心其他業務類的實現,而且減少了類膨脹的風險。 在上一篇文章Java設計模式綜合運用(門面+模版方法+責任鏈+策略)中,筆者寫了一篇門面模式、模版方法、責任鏈跟策略模式的綜合運用的事例文章,但是后來筆者發現,在實現策略模式的實現上,發現了一個弊端:那就是如果在后續業務發展中,需...
摘要:此案例中,門面類為,然后各個門面方法的參數均為抽象類,通過決定調用中的哪個子類。抽象類持有類的對象,并且實現累的一個接口是為了容器啟動完成的時候自動把相應的校驗器加入到校驗器鏈中。 引言:很久沒有更新了,主要是工作忙。最近,工作中一個子系統升級,把之前不易擴展的缺點給改進了一下,主要是運用了幾個設計模式進行稍微改造了一下。本文也同步發布至簡書,地址: https://www.jians...
摘要:在新智能合約的構造函數中,將引用我們的合約工廠的地址。以太坊,主要是針對工程師使用進行區塊鏈以太坊開發的詳解。以太坊入門教程,主要介紹智能合約與應用開發,適合入門。這里是原文用工廠模式管理多個智能合約 我們寫了一份小的計算合約作為Hello World。如果我們可以創建一個允許用戶創建自己的計數器的合約怎么辦? showImg(https://segmentfault.com/img/...
摘要:面試官要不你來手寫下單例模式唄候選者單例模式一般會有好幾種寫法候選者餓漢式簡單懶漢式在方法聲明時加鎖雙重檢驗加鎖進階懶漢式靜態內部類優雅懶漢式枚舉候選者所謂餓漢式指的就是還沒被用到,就直接初始化了對象。面試官:我看你的簡歷寫著熟悉常見的設計模式,要不你來簡單聊聊你熟悉哪幾個吧?候選者:常見的工廠模式、代理模式、模板方法模式、責任鏈模式、單例模式、包裝設計模式、策略模式等都是有所了解的候選者:...
閱讀 3034·2023-04-26 03:01
閱讀 3538·2023-04-25 19:54
閱讀 1592·2021-11-24 09:39
閱讀 1374·2021-11-19 09:40
閱讀 4250·2021-10-14 09:43
閱讀 2062·2019-08-30 15:56
閱讀 1490·2019-08-30 13:52
閱讀 1660·2019-08-29 13:05