【设计模式】职责链模式:如果第三方短信平台挂了怎么办?
任何傻瓜都能寫出計算機可以理解的代碼。好的程序員能寫出人能讀懂的代碼”—— Martin Fowler
目錄
- 鍋從天上來
- 鏈表實現(xiàn)職責(zé)鏈模式
- 使用數(shù)組實現(xiàn)職責(zé)鏈模式
- 指責(zé)連模式的變異模式
- 職責(zé)鏈模式應(yīng)用場景
- 總結(jié)
- 常見的設(shè)計模式
鍋從天上來
風(fēng)和日麗,我還是和往常一樣在自己的座位上寫著bug,聽著小曲,十分愜意,心里還想著,好久沒寫博客了,最近也沒有什么素材,正當(dāng)我發(fā)愁的時候,素材自動送上門了,又可以寫博客了。
測試小哥哥在禪道給我提了一個bug:發(fā)送短信驗證成功,但是手機一直收不到短信驗證碼,重試了很多次都不行。我靠,還能這操作?嚇得我趕緊去服務(wù)器上撈了日志,查看日志之后發(fā)現(xiàn):原來是短信服務(wù)商出現(xiàn)問題了,我們連不上短信服務(wù)商,所以導(dǎo)致短信發(fā)不出去,然后我就將這個bug直接按外部原因點了解決。
可惜啊,后來產(chǎn)品知道了這個消息之后,他偷偷的申請了兩家短信運營商,加上正在使用的這一家,一共是三家,產(chǎn)品給出了一個新的需求:發(fā)送短信驗證碼的時候如果連不上短信運營商,那么就直接換一家繼續(xù)發(fā),直到將短信發(fā)出去為止。
哎,又得加班把這個需求搞定了,一共三個短信運營商,發(fā)送短信驗證碼時,先找到第一個運營商,如果第一個運營商發(fā)送失敗,那么找到第二個運營商進行發(fā)送,如果第二個運營商發(fā)送成功,則停止操作,如果第二個運營商發(fā)送失敗,繼續(xù)往下找到第三個運營商進行發(fā)送,如果三個運行商都發(fā)送失敗,那么發(fā)送郵件告訴運營人員。
大致需求就只這樣,如果是你?你會怎么處理這個需求呢?大家可以先思考一下,然后再參考一下我下面的解決思路,可以對比一下。
話不多說,我們正式開始吧!
書到用時方恨少,這個時候知道多讀書的好處了吧,下面就是我的showtime!
什么是職責(zé)鏈模式?
責(zé)任鏈模式是一種設(shè)計模式。在責(zé)任鏈模式里,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發(fā)出這個請求的客戶端并不知道鏈上的哪一個對象最終處理這個請求,這使得系統(tǒng)可以在不影響客戶端的情況下動態(tài)地重新組織和分配責(zé)任。(百度百科)
簡單來說就是再職責(zé)鏈模式中有很多的處理器,這些處理器依次處理某一個請求,A處理器處理完交給處理器B,處理器B處理完交給處理器C,直到有處理器能夠處理這個請求為止。這么一看我們短信的這個需求是不是就可以通過職責(zé)鏈模式來呢?答案是肯定的。
我們一起來看看職責(zé)鏈模式的使用方法
鏈表實現(xiàn)職責(zé)鏈模式
package com.liuxing.handler;/*** @ProjectName: handler-of-responsibility-pattern* @Package: com.liuxing.handler* @ClassName: Handler* @Author: 流星007* @Description: 處理器抽象父類* csdn:https://blog.csdn.net/qq_33220089* 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523* @Version: 1.0*/ public abstract class Handler {/*** 下一個處理器*/protected Handler nextHandler = null;/*** 設(shè)置下一個處理器* @param nextHandler*/public void setNextHandler(Handler nextHandler) {this.nextHandler = nextHandler;}/*** 處理器處理請求*/public abstract void handler(); } package com.liuxing.handler; /*** @ProjectName: handler-of-responsibility-pattern* @Package: com.liuxing.handler* @ClassName: Handler1* @Author: 流星007* @Description: 處理器1* csdn:https://blog.csdn.net/qq_33220089* 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523* @Version: 1.0*/ public class Handler1 extends Handler {@Overridepublic void handler() {System.out.println("這是第一個handler,無法解決此問題,請求下一個處理器處理");boolean flag = false;if(flag){return ;}if(!flag && nextHandler != null){nextHandler.handler();}} } package com.liuxing.handler;/*** @ProjectName: handler-of-responsibility-pattern* @Package: com.liuxing.handler* @ClassName: Handler2* @Author: 流星007* @Description: 處理器2* csdn:https://blog.csdn.net/qq_33220089* 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523* @Version: 1.0*/ public class Handler2 extends Handler {@Overridepublic void handler() {boolean flag = false;System.out.println("這是第二個handler,無法解決此問題,請求下一個處理器處理");if(flag){return ;}if(!flag && nextHandler != null){nextHandler.handler();}} } package com.liuxing.handler;/*** @ProjectName: handler-of-responsibility-pattern* @Package: com.liuxing.handler* @ClassName: Handler3* @Author: 流星007* @Description: 處理器3* csdn:https://blog.csdn.net/qq_33220089* 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523* @Version: 1.0*/ public class Handler3 extends Handler {@Overridepublic void handler() {boolean flag = true;System.out.println("這是第三個handler,問題解決,無需在往下執(zhí)行");if(flag){return ;}if(!flag && nextHandler != null){nextHandler.handler();}System.out.println("執(zhí)行結(jié)束,沒有處理器能夠解決這個請求");} } package com.liuxing.chain;import com.liuxing.handler.Handler;/*** @ProjectName: handler-of-responsibility-pattern* @Package: com.liuxing.chain* @ClassName: ChainHandler* @Author: 流星007* @Description: 處理器鏈* csdn:https://blog.csdn.net/qq_33220089* 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523* @Version: 1.0*/ public class ChainHandler {//第一個處理器private Handler firstHandler;//最后一個處理器private Handler endHandler;/*** 添加處理器* @param handler*/public void addHandler(Handler handler){//將當(dāng)前處理器的下一個處理器設(shè)置為null,這一步也可以省略handler.setNextHandler(null);//判斷是不是第一個處理器,如果是第一個處理器,那么將firstHandler和endHandler都設(shè)置為:handlerif(firstHandler == null ){firstHandler = handler;endHandler = handler;return ;}//如果不是第一個處理器,那么將當(dāng)前處理器添加到之前最后一個處理器的下一個處理器中endHandler.setNextHandler(handler);//將最后一個處理器修改為當(dāng)前添加的這個處理器endHandler = handler;}/*** 開始執(zhí)行handler*/public void handler(){if(firstHandler == null ){return ;}firstHandler.handler();}} package com.liuxing.test;import com.liuxing.chain.ChainHandler; import com.liuxing.handler.Handler1; import com.liuxing.handler.Handler2; import com.liuxing.handler.Handler3;public class HandlerTest {public static void main(String[] args) {ChainHandler chainHandler = new ChainHandler();chainHandler.addHandler(new Handler1());chainHandler.addHandler(new Handler2());chainHandler.addHandler(new Handler3());chainHandler.handler();} }這就是職責(zé)鏈模式的demo,有一個抽象父類:Handler(處理器抽象父類);三個子類:Handler1、Handler2、Handler3;重寫父類中的handler方法,還需要一個處理器鏈:ChainHandler,處理器鏈中包含首尾兩個處理器,一個添加處理器的方法,一個執(zhí)行handler的方法,我們一起來看看這個demo的執(zhí)行結(jié)果:
這是第一個handler,無法解決此問題,請求下一個處理器處理 這是第二個handler,無法解決此問題,請求下一個處理器處理 這是第三個handler,問題解決,無需在往下執(zhí)行Process finished with exit code 0我們發(fā)現(xiàn)執(zhí)行到第三個處理器的時候就不再往下執(zhí)行了。
職責(zé)鏈模式相信大家都知道怎么使用了,我也相信大家也應(yīng)該知道短信的需求怎么改造了吧,短信的代碼改造這里我就不寫了,大家感性的話可以自己研究一下,照葫蘆畫瓢就行了,很簡單。
那這篇博客是不是就這樣結(jié)束了呢?當(dāng)然不是,重點才剛剛開始。
上面的職責(zé)鏈模式讓人看起來很別扭,那是因為還沒有優(yōu)化過,我們發(fā)現(xiàn)再三個子類中都需要調(diào)用下一個處理器對不對,這樣是不是比較麻煩,如果有人再添加處理器的時候忘記調(diào)用下一個處理器呢?bug是不是就來了,然后你就需要加班了,所以這種事情必須要扼殺在搖籃里,我們直接不在子類中調(diào)用下一個處理器,我們把調(diào)用下一個處理器放到抽象父類中,讓他統(tǒng)一處理,就能完美解決了。
請看代碼
package com.liuxing.handler;/*** @ProjectName: handler-of-responsibility-pattern* @Package: com.liuxing.handler* @ClassName: Handler* @Author: 流星007* @Description: 處理器抽象父類* csdn:https://blog.csdn.net/qq_33220089* 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523* @Version: 1.0*/ public abstract class Handler {/*** 下一個處理器*/protected Handler nextHandler = null;/*** 設(shè)置下一個處理器* @param nextHandler*/public void setNextHandler(Handler nextHandler) {this.nextHandler = nextHandler;}public final void handler(){boolean flag = doHandler();if(!flag && nextHandler!= null){nextHandler.handler();}}/*** 處理器處理請求*/protected abstract boolean doHandler(); } package com.liuxing.handler; /*** @ProjectName: handler-of-responsibility-pattern* @Package: com.liuxing.handler* @ClassName: Handler1* @Author: 流星007* @Description: 處理器1* csdn:https://blog.csdn.net/qq_33220089* 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523* @Version: 1.0*/ public class Handler1 extends Handler {@Overridepublic boolean doHandler() {System.out.println("這是第一個handler,無法解決此問題,請求下一個處理器處理");return false;} } package com.liuxing.handler;/*** @ProjectName: handler-of-responsibility-pattern* @Package: com.liuxing.handler* @ClassName: Handler2* @Author: 流星007* @Description: 處理器2* csdn:https://blog.csdn.net/qq_33220089* 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523* @Version: 1.0*/ public class Handler2 extends Handler {@Overridepublic boolean doHandler() {System.out.println("這是第二個handler,無法解決此問題,請求下一個處理器處理");return false;} } package com.liuxing.handler;/*** @ProjectName: handler-of-responsibility-pattern* @Package: com.liuxing.handler* @ClassName: Handler3* @Author: 流星007* @Description: 處理器3* csdn:https://blog.csdn.net/qq_33220089* 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523* @Version: 1.0*/ public class Handler3 extends Handler {@Overridepublic boolean doHandler() {boolean flag = true;System.out.println("這是第三個handler,問題解決,無需在往下執(zhí)行");if(flag){return true;}System.out.println("執(zhí)行結(jié)束,沒有處理器能夠解決這個請求");return false;} }其他代碼不變,我們發(fā)現(xiàn),調(diào)用下一個處理器的代碼被移動了抽象父類處理器Handler中,自立只需要安安心心的處理自己的邏輯即可,是不是降低的bug的產(chǎn)生率?
這里有個小知識點,大家直到我在抽象父類:Handler中為什么將handler();設(shè)置為final嗎?知道的可以卸載評論區(qū)哦。
使用數(shù)組實現(xiàn)職責(zé)鏈模式
這種實現(xiàn)方式比上面那種基于鏈表的實現(xiàn)方式更為簡單,也更容易理解,廢話不多說,直接上代碼
package com.liuxing.handler;/*** @ProjectName: demo* @Package: com.liuxing.handler* @ClassName: IHandler* @Author: 流星007* @Description: 處理器接口* csdn:https://blog.csdn.net/qq_33220089* 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523* @Date: 2020/7/13 17:17* @Version: 1.0*/ public interface IHandler {boolean handler(); } package com.liuxing.handler; /*** @ProjectName: handler-of-responsibility-pattern* @Package: com.liuxing.handler* @ClassName: Handler1* @Author: 流星007* @Description: 處理器1* csdn:https://blog.csdn.net/qq_33220089* 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523* @Version: 1.0*/ public class Handler1 implements IHandler {@Overridepublic boolean handler() {System.out.println("這是第一個handler,無法解決此問題,請求下一個處理器處理");return false;} } package com.liuxing.handler;/*** @ProjectName: handler-of-responsibility-pattern* @Package: com.liuxing.handler* @ClassName: Handler2* @Author: 流星007* @Description: 處理器2* csdn:https://blog.csdn.net/qq_33220089* 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523* @Version: 1.0*/ public class Handler2 implements IHandler {@Overridepublic boolean handler() {System.out.println("這是第二個handler,無法解決此問題,請求下一個處理器處理");return false;} } package com.liuxing.handler;/*** @ProjectName: handler-of-responsibility-pattern* @Package: com.liuxing.handler* @ClassName: Handler3* @Author: 流星007* @Description: 處理器3* csdn:https://blog.csdn.net/qq_33220089* 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523* @Version: 1.0*/ public class Handler3 implements IHandler{@Overridepublic boolean handler() {boolean flag = true;System.out.println("這是第三個handler,問題解決,無需在往下執(zhí)行");if(flag){return true;}System.out.println("執(zhí)行結(jié)束,沒有處理器能夠解決這個請求");return false;} } package com.liuxing.chain;import com.liuxing.handler.Handler; import com.liuxing.handler.IHandler; import org.springframework.util.CollectionUtils;import java.util.ArrayList; import java.util.List;/*** @ProjectName: handler-of-responsibility-pattern* @Package: com.liuxing.chain* @ClassName: ChainHandler* @Author: 流星007* @Description: 處理器鏈* csdn:https://blog.csdn.net/qq_33220089* 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523* @Version: 1.0*/ public class ChainHandler {/*** 所有的處理器*/private List<IHandler> list = new ArrayList<IHandler>();/*** 添加處理器* @param handler*/public void addHandler(IHandler handler){list.add(handler);}/*** 開始執(zhí)行handler*/public void handler(){if(CollectionUtils.isEmpty(list)){return ;}for(IHandler handler: list){if(handler.handler()){return;}}} } package com.liuxing.test;import com.liuxing.chain.ChainHandler; import com.liuxing.handler.Handler1; import com.liuxing.handler.Handler2; import com.liuxing.handler.Handler3;public class HandlerTest {public static void main(String[] args) {ChainHandler chainHandler = new ChainHandler();chainHandler.addHandler(new Handler1());chainHandler.addHandler(new Handler2());chainHandler.addHandler(new Handler3());chainHandler.handler();} }好了,代碼改造完成,這種方式相對第一種方式來說,理解起來更加的容易,因為ArrayList是有序的,所以循環(huán)執(zhí)行的時候按照先添加的先執(zhí)行,想要控制處理器的執(zhí)行順序,只需要控制list的添加順序即可,簡單方便。
指責(zé)連模式的變異模式
職責(zé)鏈模式再開發(fā)過程中漸漸的派生出一個新的變異模式,什么樣的模式呢?正常職責(zé)鏈模式:只要有處理器能夠處理這個請求,就結(jié)束當(dāng)前流程,而變異版本就是不管當(dāng)前處理器能不能處理這個請求,都要依次往下執(zhí)行,直到所有的處理器都被執(zhí)行。這種變異模式再游戲中的敏感詞過濾中比較常見。
職責(zé)鏈模式應(yīng)用場景
1.過濾游戲中的敏感詞匯
2.servlet中的handler也是采用了職責(zé)鏈模式哦,感興趣的小伙伴可以去看看。
3.較多的switch-case也能使用指責(zé)連模式進行優(yōu)化哦。
總結(jié)
什么是指責(zé)連模式?
責(zé)任鏈模式是一種設(shè)計模式。在責(zé)任鏈模式里,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發(fā)出這個請求的客戶端并不知道鏈上的哪一個對象最終處理這個請求,這使得系統(tǒng)可以在不影響客戶端的情況下動態(tài)地重新組織和分配責(zé)任。(百度百科)
簡單來說就是再職責(zé)鏈模式中有很多的處理器,這些處理器依次處理某一個請求,A處理器處理完交給處理器B,處理器B處理完交給處理器C,直到有處理器能夠處理這個請求為止。
職責(zé)鏈變異模式
與正常的職責(zé)鏈模式相比,它需要執(zhí)行完所有的處理器,再游戲敏感詞過濾中較為常見。
常見的設(shè)計模式
【設(shè)計模式】單例模式
【設(shè)計模式】工廠模式:你還在使用一堆的if/else創(chuàng)建對象嗎?
【設(shè)計模式】建造者模式:你創(chuàng)建對象的方式有它絲滑嗎?
【設(shè)計模式】原型模式:如何快速的克隆出一個對象?
【設(shè)計模式】策略模式:我是一個有謀略的類
【設(shè)計模式】觀察者模式:一個注冊功能也能使用到設(shè)計模式?
【設(shè)計模式】門面模式:接口就像門面,一眼就能看出你的代碼水平
總結(jié)
以上是生活随笔為你收集整理的【设计模式】职责链模式:如果第三方短信平台挂了怎么办?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OceanBase使用 OBD 自动化部
- 下一篇: 成绩排序(一)c++