溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

什么是責(zé)任鏈模式

發(fā)布時(shí)間:2021-06-23 15:26:03 來源:億速云 閱讀:164 作者:chen 欄目:web開發(fā)

這篇文章主要講解了“什么是責(zé)任鏈模式”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“什么是責(zé)任鏈模式”吧!

前言

面試經(jīng)歷大家肯定都有過,但是面試的流程其實(shí)跟一種設(shè)計(jì)模式很像,每一輪的面試官都有自己的職責(zé),一個(gè)求職者面試經(jīng)歷的過程就好比一次客戶端的請(qǐng)求過程。

在設(shè)計(jì)模式系列的文章中之前已經(jīng)為大家分享了創(chuàng)建型設(shè)計(jì)模式,感興趣的小伙伴們可以再去翻看之前的分享。接下來開始分享設(shè)計(jì)模式三大類型中的行為型模式了,今天要分享的是責(zé)任鏈模式

大綱

什么是責(zé)任鏈模式

定義

什么是責(zé)任鏈?它的原理是什么?

  • 將請(qǐng)求的發(fā)送和接收解耦,讓多個(gè)接收對(duì)象都有機(jī)會(huì)處理這個(gè)請(qǐng)求。將這些接收對(duì)象串成一條鏈,并沿著這條鏈傳遞這個(gè)請(qǐng)求,直到鏈上的某個(gè)接收對(duì)象能夠處理它為止。

  • 以上定義來自《設(shè)計(jì)模式之美》

再看看一張官方圖解吧

什么是責(zé)任鏈模式

  • Client(客戶端):實(shí)例化一個(gè)處理器的鏈,在第一個(gè)鏈對(duì)象中調(diào)用handleRequest 方法。

  • Handle(處理器):抽象類,提供給實(shí)際處理器繼承然后實(shí)現(xiàn)handleRequst方法,處理請(qǐng)求

  • ConcreteHandler(具體處理器):繼承了handler的類,同時(shí)實(shí)現(xiàn)handleRequst方法,負(fù)責(zé)處理業(yè)務(wù)邏輯類,不同業(yè)務(wù)模塊有不同的ConcreteHandler。

這么看結(jié)構(gòu)其實(shí)還是比較簡單的,但是我們還是拿面試的流程來模擬一下責(zé)任鏈吧!

代碼實(shí)現(xiàn)

假設(shè)現(xiàn)在去一家公司面試,第一次去一面,第二次去二面,第三次去直接過了。那這個(gè)模擬面試代碼怎么寫呢?

public abstract class Handler {      protected Handler handler;      public void setHandler(Handler handler) {         this.handler = handler;     }     public abstract void handleRequest(Integer times); }

首先我們還是定義一個(gè)抽象Handler處理器,同時(shí)添加一個(gè)抽象處理方法  handleRequest,后面我只需要編寫具體的處理器來繼承Handler類

public class FirstInterview extends Handler {     @Override     public void handleRequest(Integer times) {         // 條件判斷是否是屬于當(dāng)前Handler的處理范圍之內(nèi),不是則向下傳遞Handler處理器         if(times ==1){           // 假設(shè)這里是處理的業(yè)務(wù)邏輯代碼             System.out.println("第一次面試"+times);         }         handler.handleRequest(times);     } }

其次構(gòu)建第一次面試Handler,內(nèi)部實(shí)現(xiàn)handleRequest方法,判斷一下是否是當(dāng)前處理應(yīng)該處理的業(yè)務(wù)邏輯,不是則向下傳遞。同樣的第二次的SecondInterview和FirstInterview代碼基本是一致的,我就不給大家貼出來了,直接看最后一個(gè)

public class ThreeInterview extends Handler {     @Override     public void handleRequest(Integer times) {         if (times == 3) {             System.out.println("第三次面試"+ times + ",恭喜面試通過,HR會(huì)跟你聯(lián)      系?。?!");         }     }      public static void main(String[] args) {         Handler first = new FirstInterview();         Handler second = new SecondInterview();         Handler three = new ThreeInterview();         first.setHandler(second);         second.setHandler(three);          // 第一次面試         first.handleRequest(1);         System.out.println();         // 第二次面試         first.handleRequest(2);         System.out.println();         // 第三次面試         first.handleRequest(3);         System.out.println();     } }

 什么是責(zé)任鏈模式

這個(gè)結(jié)果可以很明顯的看出,根據(jù)我們傳參,不同的Handler根據(jù)自己的職責(zé)處理著自己的業(yè)務(wù),這就是責(zé)任鏈。

框架的應(yīng)用

責(zé)任鏈在很多框架源碼中也有體現(xiàn)。比如開始學(xué)SpringMVC中的 ServletFilter

以及Spring中的 SpringInterceptor 這里面其實(shí)都是運(yùn)用了責(zé)任鏈模式的思想,達(dá)到框架的可擴(kuò)展性的同時(shí)也遵循著開閉原則。

作為常見的RPC框架的DUBBO其實(shí)里面也同樣有這個(gè)責(zé)任鏈的思想。

給大家一個(gè)思考問題?

  • dubbo服務(wù)一旦暴露出去了,那么基本任何服務(wù)都能調(diào)用,但是在一些特殊的業(yè)務(wù)中需要我們暴露服務(wù),但是又不希望被不了解業(yè)務(wù)的人隨便調(diào)用。

  • 比如:商品的庫存修改的dubbo服務(wù),我們只允許下單,購物車,添加修改商品等一些指定場景可以調(diào)用。

  • 那么有什么辦法,在Provider這端做好攔截,針對(duì)特定的服務(wù)才允許調(diào)用,否則攔截下來不允許執(zhí)行?

第一種方法,添加服務(wù)名稱APP_NAME作為傳參校驗(yàn),這是很常見也最容易想到的辦法。

第二種方法,實(shí)現(xiàn)一個(gè)DUBBO攔截器,對(duì)RPC調(diào)用進(jìn)行選擇性過濾。

針對(duì)上面的兩種方法,給大家詳細(xì)講講第二種方法具體怎么實(shí)現(xiàn),每個(gè)公司都會(huì)基于現(xiàn)有的DUBBO源碼做自己的特定化改動(dòng),那么第二種方式也是同樣需要我們改動(dòng)線有dubbo源碼。

先修改ConsumerContextFilter消費(fèi)者攔截器

什么是責(zé)任鏈模式

這里我們以dubbo的2.7.19版本為例。在ConsumerContextFilter中添加APP_NAME至Attachments中,那么作為本次的RPC調(diào)用都能從Attachments中獲取到我們?nèi)氲闹怠?/p>

至于這個(gè)APP_NAME的獲取 可以通過 System.getProperty("project.name", "") 來獲取服務(wù)名

  • 這里我就不對(duì)DUBBO做過多的展開,大家如果有強(qiáng)烈建議講解。那么在結(jié)束設(shè)計(jì)模式再跟大家詳細(xì)剖析一下dubbo,以及zookeeper里面的ZAB,一致性選舉算法等等。

CONSUMER既然已經(jīng)填充了服務(wù)名稱,那么在Provider同樣的也就只需要寫一個(gè)ProviderFilter 就可以了

什么是責(zé)任鏈模式

這里就基本實(shí)現(xiàn)怎么處理每一次RPC調(diào)用的攔截了,然后想要那個(gè)服務(wù)攔截,在provider里面的filter里面指定一下這個(gè)DubboProviderFilter就可以了,也可以全局都實(shí)現(xiàn)。

注意 :這個(gè)Filter 要是用DUBBO包里面的,不要搞錯(cuò)了。

現(xiàn)實(shí)業(yè)務(wù)改造舉例

框架中既然都有這種思想,那么怎么運(yùn)用到業(yè)務(wù)代碼中呢?

還是給大家舉一個(gè)例子:

商品詳情展示我們可以是分模塊展示的,比如頭圖,商品信息,sku信息,配送地址,分期付費(fèi)等等。

那么怎么進(jìn)行組裝到商品詳情的展示呢?

public abstract class AbstractDataHandler<T> {      // 處理模塊化數(shù)據(jù)     protected abstract T doRequest(String query) throws Exception; }

首先我們還是定一個(gè)抽象數(shù)據(jù)Handler,然后分別建立ItemInfoHandler 和SkuInfoHandler 來繼承抽象處理器

@Component public class ItemInfoHandler extends AbstractDataHandler<ItemInfoHandler.ItemInfo> {     @Override     protected ItemInfoHandler.ItemInfo doRequest(String query) {         ItemInfoHandler.ItemInfo info = new ItemInfo();         info.setItemId(123456L);         info.setItemName("測試商品");         return info;     }      @Data     public static class ItemInfo {         private Long itemId;         private String itemName;     } }

同樣SkuInfoHandler類也是一樣的

@Component public class SkuInfoHandler extends AbstractDataHandler<SkuInfoHandler.SkuInfo> {     @Override     protected SkuInfoHandler.SkuInfo doRequest(String query) {         SkuInfoHandler.SkuInfo info = new SkuInfoHandler.SkuInfo();         info.setSkuId(78910L);         info.setSkuName("測試SKU");         return info;     }     @Data     public static class SkuInfo {         private Long skuId;         private String skuName;     } }

最后就是我們的測試代碼了

@Component public class DataAggregation {     @Autowired     private SkuInfoHandler skuInfoHandler;     @Autowired     private ItemInfoHandler itemInfoHandler;      public Map convertItemDetail() throws Exception {        Map result = new HashMap();        result.put("skuInfoHandler", skuInfoHandler.doRequest("模擬數(shù)據(jù)請(qǐng)求"));        result.put("itemInfoHandler",itemInfoHandler.doRequest("模擬數(shù)據(jù)請(qǐng)求"));        return result;     }      public static void main(String[] args) throws Exception {         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");         DataAggregation dataAggregation = (DataAggregation) applicationContext.getBean("dataAggregation");         Map map = dataAggregation.convertItemDetail();         System.out.println(JSON.toJSONString(map));       // 打印的結(jié)果數(shù)據(jù)       // {"skuInfoHandler":{"skuId":78910,"skuName":"測試SKU"},"itemInfoHandler":{"itemId":123456,"itemName":"測試商品"}}     } }

這個(gè)例子其實(shí)是經(jīng)過一點(diǎn)小小的改動(dòng)的,我們沒有通過向下傳遞處理器的方式,而是通過實(shí)際業(yè)務(wù)邏輯在 convertItemDetail  的方法中去構(gòu)建每個(gè)模塊的數(shù)據(jù),最后返回出一個(gè)Map結(jié)構(gòu)數(shù)據(jù)。

這里其實(shí)還有另外的一種寫法,把每一個(gè)需要處理的Handler  可以加載到一個(gè)List容器中,然后循環(huán)調(diào)用每個(gè)Handler中的doRequest方法,當(dāng)然這是針對(duì)一些其他的業(yè)務(wù)場景這么寫。

看完大家也能發(fā)現(xiàn)其實(shí)每個(gè)Handler是可以共用的,每一塊業(yè)務(wù)的代碼邏輯非常的清晰,這樣的代碼寫出來就感覺很舒服了。

總結(jié)

設(shè)計(jì)模式不是一成不變的,只有適合自己當(dāng)前業(yè)務(wù)的模式才是最好的模式。理解前輩的思想,組合我們自己需要的模式。

感謝各位的閱讀,以上就是“什么是責(zé)任鏈模式”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)什么是責(zé)任鏈模式這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI