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