java?設(shè)計(jì)模式從風(fēng)控鏈理解責(zé)任鏈模式
前言
責(zé)任鏈?zhǔn)且环N行為型模式。顧名思義,由多個(gè)有不同處理能力節(jié)點(diǎn)組成的一條執(zhí)行鏈。當(dāng)一個(gè)事件進(jìn)來時(shí),會(huì)判斷當(dāng)前節(jié)點(diǎn)是否有處理的能力,反之轉(zhuǎn)入下一個(gè)節(jié)點(diǎn)進(jìn)行處理??梢詮闹Ц兜娘L(fēng)控鏈這個(gè)場(chǎng)景,深入的理解責(zé)任鏈模式。
定義
責(zé)任鏈模式:包含了一些命令和一系列的處理對(duì)象。每一個(gè)處理對(duì)象決定了它能處理哪些命令對(duì)象,它也知道如何將它不能處理的命令對(duì)象傳遞給該鏈中的下一個(gè)處理對(duì)象。
如何理解
信用卡套現(xiàn),花唄套現(xiàn),白條套現(xiàn),類似的名詞對(duì)于我們來講應(yīng)該不陌生吧。在支付系統(tǒng)中會(huì)有一套風(fēng)控系統(tǒng),來避免發(fā)生類似的事情出現(xiàn)。而風(fēng)控系統(tǒng)就是責(zé)任鏈模式的一種應(yīng)用。
比如說給xxx商戶配置的風(fēng)控規(guī)則是:
TOP | 信用卡 | 花唄 | 白條 |
---|---|---|---|
木屋燒烤 | 500/日 | 300/日 | 300/日 |
當(dāng)用戶向商家付款時(shí),會(huì)根據(jù)不同的支付方式,來判斷是否觸發(fā)風(fēng)控條件。如果達(dá)到觸發(fā)條件則不允許使用該方式支付。來避免發(fā)生信用卡套現(xiàn)類似的事情。
我們寫個(gè)簡(jiǎn)單的風(fēng)控程序來理解一下:
UML
代碼示例
RiskHandler
public abstract class RiskHandler { /** * 支付方式 */ private String payType; private RiskHandler nextHandler; public RiskHandler(String payType) { this.payType = payType; } /** * 是否能支付 * @param order 訂單 */ protected abstract boolean canPay(Order order); /** * 處理器 * @param order 訂單 * @return 返回true 或者 false */ public final boolean handler(Order order){ if(order.getPayType().equals(this.payType)){ return this.canPay(order); }else { if (this.nextHandler != null){ return this.nextHandler.handler(order); }else{ throw new IllegalArgumentException("支付方式有誤"); } } } public void setNextHandler(RiskHandler handler){ this.nextHandler = handler; } }
CreditHandler
public class CreditHandler extends RiskHandler { /** * 500 限額 */ private BigDecimal limitAmount = BigDecimal.valueOf(500); public CreditHandler() { super(PayTypeEnum.CREDIT.getPayType()); } @Override protected boolean canPay(Order order) { if(order.getAmount().compareTo(limitAmount) < 0){ limitAmount = limitAmount.subtract(order.getAmount()); return true; }else { return false; } } }
HuabeiHandler
public class HuabeiHandler extends RiskHandler { /** * 300 限額 */ private BigDecimal limitAmount = BigDecimal.valueOf(300); public HuabeiHandler() { super(PayTypeEnum.HUA_BEI.getPayType()); } @Override protected boolean canPay(Order order) { if(order.getAmount().compareTo(limitAmount) < 0){ limitAmount = limitAmount.subtract(order.getAmount()); return true; }else { return false; } } }
BaiTiaoHandler
public class BaiTiaoHandler extends RiskHandler { /** * 300 限額 */ private BigDecimal limitAmount = BigDecimal.valueOf(300); public BaiTiaoHandler() { super(PayTypeEnum.BAI_TIAO.getPayType()); } @Override protected boolean canPay(Order order) { if(order.getAmount().compareTo(limitAmount) < 0){ limitAmount = limitAmount.subtract(order.getAmount()); return true; }else { return false; } } }
Order
public interface Order { /** * 獲取支付方式 * @return 支付方式 */ String getPayType(); /** * 獲取訂單金額 * @return 訂單金額 */ BigDecimal getAmount(); }
ConsumeOrder
public class ConsumeOrder implements Order { private String payType; private BigDecimal amount; public ConsumeOrder(String payType, BigDecimal amount) { this.payType = payType; this.amount = amount; } @Override public String getPayType() { return this.payType; } @Override public BigDecimal getAmount() { return this.amount; } }
PayTypeEnum
public enum PayTypeEnum { /** * 花唄 */ HUA_BEI("hua_bei"), /** * 白條 */ BAI_TIAO("bai_tiao"), /** * 信用卡 */ CREDIT("credit"), ; private String payType; PayTypeEnum(String payType) { this.payType = payType; } public String getPayType() { return payType; } }
PayRiskControlService
public class PayRiskControlService { public boolean canPay(Order order){ // 設(shè)置風(fēng)控 RiskHandler creditHandler = new CreditHandler(); RiskHandler huabeiHandler = new HuabeiHandler(); RiskHandler baiTiaoHandler = new BaiTiaoHandler(); creditHandler.setNextHandler(huabeiHandler); huabeiHandler.setNextHandler(baiTiaoHandler); return creditHandler.handler(order); } }
Test
public class Test { public static void main(String[] args) { // 花唄訂單 ConsumeOrder huabeiOrder = new ConsumeOrder( PayTypeEnum.HUA_BEI.getPayType(), BigDecimal.valueOf(200) ); // 白條訂單 ConsumeOrder baitiaoOrder = new ConsumeOrder( PayTypeEnum.BAI_TIAO.getPayType(), BigDecimal.valueOf(301) ); // 加載風(fēng)控系統(tǒng) PayRiskControlService riskControlService = new PayRiskControlService(); // 創(chuàng)建訂單 boolean canPayOfHuabei = riskControlService.canPay(huabeiOrder); boolean canPayOfBaitiao = riskControlService.canPay(baitiaoOrder); System.out.println("canPayOfHuabei = " + canPayOfHuabei); System.out.println("canPayOfBaitiao = " + canPayOfBaitiao); } }
測(cè)試結(jié)果
ConsumeOrder{payType='hua_bei', amount=200} canPay: true
ConsumeOrder{payType='bai_tiao', amount=301} canPay: false
優(yōu)點(diǎn)
- 降低耦合度,將請(qǐng)求和處理分開。
- 簡(jiǎn)化了對(duì)象,是對(duì)象不需要知道鏈的結(jié)構(gòu)。
- 增強(qiáng)給對(duì)象指派職責(zé)的靈活性,通過改變鏈內(nèi)的成員或者調(diào)動(dòng)他們的次序,運(yùn)行動(dòng)態(tài)的新增或者刪除責(zé)任。
- 增加新的處理類很方便。
缺點(diǎn)
- 不能保證請(qǐng)求一定被接收
- 系統(tǒng)性能將受到一定影響,而且在調(diào)試代碼時(shí)不太方便,可能會(huì)造成循環(huán)調(diào)用。
- 可能不容易觀察運(yùn)行時(shí)的特征,有礙于除錯(cuò)。
應(yīng)用場(chǎng)景
- 有多個(gè)對(duì)象可以處理同一個(gè)請(qǐng)求,具體是哪個(gè)對(duì)象處理由該請(qǐng)求運(yùn)行時(shí)刻自動(dòng)確定。
- 在不明確接受者的情況下,向多個(gè)對(duì)象中的一個(gè)提交一個(gè)請(qǐng)求。
- 可以動(dòng)態(tài)指定一組對(duì)象處理請(qǐng)求。
責(zé)任鏈模式的兩種情況
純的職責(zé)鏈模式
一個(gè)請(qǐng)求必須被某一個(gè)處理者對(duì)象所接收,且一個(gè)具體處理者對(duì)某個(gè)請(qǐng)求的處理只能采用以下兩種行為之一:自己處理(承擔(dān)責(zé)任),把責(zé)任推給下家處理。
不純的職責(zé)鏈模式
允許出現(xiàn)某一個(gè)具體處理者對(duì)象在承擔(dān)了請(qǐng)求的一部分責(zé)任后又將剩余的責(zé)任傳給下家的情況,且一個(gè)請(qǐng)求可以最終不被任何接收端對(duì)象所接收。比如說,多級(jí)審核,登錄安全監(jiān)測(cè)。
注意的點(diǎn)
鏈中節(jié)點(diǎn)數(shù)量需要控制,避免出現(xiàn)超長(zhǎng)鏈的情況,一般的做法是在Handler中設(shè)置一個(gè)最大節(jié)點(diǎn)數(shù)量,在setNext方法中判斷是否已經(jīng)是超過其閾值,超過則不允許該鏈建立,避免無意識(shí)地破壞系統(tǒng)性能。
參考文章
- 設(shè)計(jì)模式之禪道第二版
- 責(zé)任鏈設(shè)計(jì)模式|菜鳥教程
以上就是java 設(shè)計(jì)模式從風(fēng)控鏈理解責(zé)任鏈模式的詳細(xì)內(nèi)容,更多關(guān)于java責(zé)任鏈模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java Tree結(jié)構(gòu)數(shù)據(jù)中查找匹配節(jié)點(diǎn)方式
這篇文章主要介紹了Java Tree結(jié)構(gòu)數(shù)據(jù)中查找匹配節(jié)點(diǎn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09Spring MVC中處理ajax請(qǐng)求的跨域問題與注意事項(xiàng)詳解
跨域問題是我們大家在開發(fā)中會(huì)經(jīng)常遇到的一個(gè)問題,所以下面這篇文章主要給大家介紹了關(guān)于Spring MVC中處理ajax請(qǐng)求的跨域問題與注意事項(xiàng)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11SpringBoot2.0整合Shiro框架實(shí)現(xiàn)用戶權(quán)限管理的示例
這篇文章主要介紹了SpringBoot2.0整合Shiro框架實(shí)現(xiàn)用戶權(quán)限管理的示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08Spring Boot實(shí)現(xiàn)圖片上傳/加水印一把梭操作實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于Spring Boot實(shí)現(xiàn)圖片上傳/加水印一把梭操作的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11Redis中4種延時(shí)隊(duì)列實(shí)現(xiàn)方式小結(jié)
Redis作為高性能的內(nèi)存數(shù)據(jù)庫(kù),具備原子操作、數(shù)據(jù)結(jié)構(gòu)豐富和簡(jiǎn)單易用的特性,本文將介紹基于Redis實(shí)現(xiàn)分布式延時(shí)隊(duì)列的四種方式,大家可以根據(jù)需要進(jìn)行選擇2025-04-04MyBatis-Plus?分頁(yè)查詢的實(shí)現(xiàn)示例
本文主要介紹了MyBatis-Plus?分頁(yè)查詢的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03Java 配置log4j日志文件路徑 (附-獲取當(dāng)前類路徑的多種操作)
這篇文章主要介紹了Java 配置log4j日志文件路徑 (附-獲取當(dāng)前類路徑的多種操作),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-10-10