責任鏈模式CoR (Chain of Responsibility)

責任鏈模式CoR (Chain of Responsibility)

概述

????責任鏈模式是一種設(shè)計模式。在責任鏈模式里,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發(fā)出這個請求的客戶端并不知道鏈上的哪一個對象最終處理這個請求,這使得系統(tǒng)可以在不影響客戶端的情況下動態(tài)地重新組織和分配責任。

????責任鏈可以是一條直線、一個環(huán)或者一個樹形結(jié)構(gòu),客戶端實際上無須關(guān)心請求的處理細節(jié)以及請求的傳遞,只需將請求發(fā)送到鏈上即可,從而實現(xiàn)請求發(fā)送者和請求處理者解耦。?

對責任鏈的理解,關(guān)鍵在于對鏈的理解,即包含如下兩點:

????1.鏈是一系列節(jié)點的集合,在責任鏈中,節(jié)點實質(zhì)上是指請求的處理者;

????2.鏈的各節(jié)點可靈活拆分再重組,在責任鏈中,實質(zhì)上就是請求發(fā)送者與請求處理者的解耦。


在什么情況下使用責任鏈模式

  在下面的情況下使用責任鏈模式:

  第一、系統(tǒng)已經(jīng)有一個由處理者對象組成的鏈。這個鏈可能由復(fù)合模式給出,

  第一、當有多于一個的處理者對象會處理一個請求,而且在事先并不知道到底由哪一個處理者對象處理一個請求。這個處理者對象是動態(tài)確定的。

  第二、當系統(tǒng)想發(fā)出一個請求給多個處理者對象中的某一個,但是不明顯指定是哪一個處理者對象會處理此請求。

  第三、當處理一個請求的處理者對象集合需要動態(tài)地指定時。


主要優(yōu)點

職責鏈模式的主要優(yōu)點如下:

????(1) 職責鏈模式使得一個對象無須知道是其他哪一個對象處理其請求,對象僅需知道該請求會被處理即可,接收者和發(fā)送者都沒有對方的明確信息,且鏈中的對象不需要知道鏈的結(jié)構(gòu),由客戶端負責鏈的創(chuàng)建,降低了系統(tǒng)的耦合度。

????(2) 請求處理對象僅需維持一個指向其后繼者的引用,而不需要維持它對所有的候選處理者的引用,可簡化對象的相互連接。

????(3) 在給對象分派職責時,職責鏈可以給我們更多的靈活性,可以通過在運行時對該鏈進行動態(tài)的增加或修改來增加或改變處理一個請求的職責。哪一個對象最終處理一個命令可以因為由那些對象參加責任鏈、以及這些對象在責任鏈上的位置不同而有所不同。

????(4) 在系統(tǒng)中增加一個新的具體請求處理者時無須修改原有系統(tǒng)的代碼,只需要在客戶端重新建鏈即可,從這一點來看是符合“開閉原則”的。

主要缺點

職責鏈模式的主要缺點如下:

????(1) 由于一個請求沒有明確的接收者,那么就不能保證它一定會被處理,該請求可能一直到鏈的末端都得不到處理;一個請求也可能因職責鏈沒有被正確配置而得不到處理。

????(2) 對于比較長的職責鏈,請求的處理可能涉及到多個處理對象,系統(tǒng)性能將受到一定影響,而且在進行代碼調(diào)試時不太方便。

????(3) 如果建鏈不當,可能會造成循環(huán)調(diào)用,將導致系統(tǒng)陷入死循環(huán)。

純的與不純的責任鏈模式

????一個純的責任鏈模式要求一個具體的處理者對象只能在兩個行為中選擇一個:一是承擔責任,而是把責任推給下家。不允許出現(xiàn)某一個具體處理者對象在承擔了一部分責任后又 把責任向下傳的情況。

????在一個純的責任鏈模式里面,一個請求必須被某一個處理者對象所接收;在一個不純的責任鏈模式里面,一個請求可以最終不被任何接收端對象所接收。

????純的責任鏈模式的實際例子很難找到,一般看到的例子均是不純的責任鏈模式的實現(xiàn)。

實現(xiàn)

????職責鏈模式需要一個總接口,用來定義處理對象的公共部分(一般使用抽象類來定義),公共部分包括:一個后繼處理器,設(shè)置和獲取后繼處理器的方法,具體的請求處理方法(這個方法需要在每個具體處理對象中實現(xiàn)),這里定義為抽象方法。

已有實現(xiàn)的例子

????在實際軟件開發(fā)中,如果遇到有多個對象可以處理同一請求時可以考慮使用職責鏈模式,最常見的例子包括在 Java Web 應(yīng)用開發(fā)中創(chuàng)建一個過濾器(Filter)鏈來對請求數(shù)據(jù)進行過濾(中文字符亂碼的處理)、在工作流系統(tǒng)中實現(xiàn)公文的分級審批、在Struts應(yīng)用中添加不同的攔截器(常用的有類型轉(zhuǎn)化、異常處理,數(shù)據(jù)校驗…)以增強Struts2的功能等,還有下面dom事件處理也是責任鏈模式。

DHTML中的事件處理

????瀏覽器的DOM(Document Object Model)模型中的事件處理均采用責任鏈模式。

 Netscape的事件模型

  Netscape的事件處理機制叫做“事件捕捉”(Event Capturing)。在事件捕捉機制里面,一個事件是從DOM的最高一層向下傳播,也就是說,window對象是第一個接到事件的,然后是document對象,如此往下---事件的產(chǎn)生對象反而是最后一個接到事件的。

  如果要是一個對象捕獲某一個事件,只需要調(diào)用captureEvent()方法;如果要使一個對象把某一個事件向下傳而不處理此事件,只需要對此對象使用releaseEvents方法即可。下面考察一個簡單的事件捕獲和傳遞的例子。

? ??Internet Explorer的事件模型

  Internet Explorer處理事件的方式與Netscape既相似又不同。當一個事件發(fā)生在Internet Explorer所瀏覽的網(wǎng)頁中時,Internet Explorer會使用DHTML的“Event Bubbling”即事件浮升機制處理此事件。Internet Explorer的DOM模型是html對象等級結(jié)構(gòu)和事件處理機制。在DOM里面,每一個html標示都是一個DOM對象,而每一個DOM對象都可以產(chǎn)生事先定義好的幾個事件中的一個(或幾個)。這樣的一個事件會首先發(fā)生在事件所屬的對象上,然后向上傳播,傳到此對象所屬的容器對象上,如此等等。因此,事件浮升機制恰恰是事件捕捉機制的相反面。

  ?在Event Bubbling機制里面,產(chǎn)生事件的對象首先會收到事件。然后,事件會依照對象的等級結(jié)構(gòu)向上傳播。比如一個DIV里有一個Form,F(xiàn)orm里面又有一個Button,那么當Button的onclick事件產(chǎn)生時,F(xiàn)orm的onclick事件代碼就會被執(zhí)行。然后,事件就會傳到DIV對象。如果DIV對象的onclick事件有任何代碼的話,這代碼就會被執(zhí)行,然后事件繼續(xù)沿著DOM結(jié)構(gòu)上行。

  如果要阻止事件繼續(xù)向上傳播,可以在事件鏈的任何一個節(jié)點上把cancelBubble性質(zhì)設(shè)置成True即可。

  Internet Explorer?瀏覽器幾乎為所有的?HTML?標識符都提供了事件句柄,因此Internet Explorer不需要captureEvents()方法和releaseEvents()方法來捕獲和釋放事件。下面的JavaScript語句指定了document對象的onclick事件的處理方法。


AOP理念與CoR模式

????用傳統(tǒng)的面向?qū)ο蠓椒▽崿F(xiàn)責任鏈模式雖然能夠滿足責任鏈模式要求的一切特征,在應(yīng)用上也有很多實例,但是仍然存在者一些明顯的缺陷和不足。比如,各個請求處理者除了實現(xiàn)自身應(yīng)當處理的邏輯外還要實現(xiàn)責任鏈的結(jié)構(gòu)(即successor屬性及其Setter),也就是說,責任鏈的建立和指派包含在實現(xiàn)角色的類中,并沒有抽象出來,這直接導致責任鏈的指派不夠靈活。

? ??AOP 思想的精髓能夠?qū)M向的關(guān)注點分離出來,這大大提高了我們認識世界和抽象世界的能力。實際上,責任鏈模式的缺陷主要在于具體實現(xiàn)角色的對象中存在著共同的行為——實現(xiàn)責任鏈結(jié)構(gòu)的行為,而這些行為并沒有被抽象出來,而用?AOP 改進責任鏈模式的關(guān)鍵就是要將責任鏈結(jié)構(gòu)的實現(xiàn)用切面抽象出來,使得各個對象只關(guān)注自身必須實現(xiàn)的功能性需求。實際上,用AOP思想實現(xiàn)責任鏈模式時仍然保留了 Client,Handler 和 ConcreteHandler 三個角色,不同點是增加了實現(xiàn)責任鏈的切面,即 HandlerChain,利用AOP理念來改進責任鏈模式可以準確地分離出責任鏈模式中不同角色的共同行為。

java web filter實現(xiàn)

????當客戶端發(fā)出Web資源的請求時,Web服務(wù)器根據(jù)應(yīng)用程序配置文件設(shè)置的過濾規(guī)則進行檢查,若客戶請求滿足過濾規(guī)則,則對客戶請求/響應(yīng)進行過濾(攔截),期間我們可以對請求信息 (請求頭和請求數(shù)據(jù))進行檢查或改動,然后對請求放行以便由過濾鏈中的其他過濾器進行處理,最后把請求/響應(yīng)交給請求的Web資源(Servlet)處理。同樣地,在這個過程中我們可以修改響應(yīng)信息,從而完成一定的任務(wù)。

 過濾鏈的好處是,發(fā)出請求的客戶端并不知道鏈上的哪一個過濾器將處理這個請求,這使得系統(tǒng)可以在不影響客戶端的情況下?動態(tài)地重新組織鏈和分配責任,并且在執(zhí)行過程中的任何時候都可以打斷,只要不執(zhí)行chain.doFilter()就不會再執(zhí)行后面的過濾器和請求的內(nèi)容,這顯然可以看作是?非純責任鏈模式?的一種典型實現(xiàn)。

? ??實質(zhì)上,F(xiàn)ilter 的實現(xiàn)既體現(xiàn)了AOP的理念,也體現(xiàn)了責任鏈模式的精髓。AOP的主要的意圖是將日志記錄、性能統(tǒng)計、安全控制、事務(wù)處理、異常處理等代碼從業(yè)務(wù)邏輯代碼中劃分出來,通過對這些行為的分離,我們希望可以將它們獨立到非主導業(yè)務(wù)邏輯的方法中,進而改變這些行為的時候不影響業(yè)務(wù)邏輯的代碼。以處理中文字符亂碼問題為例,它并非是業(yè)務(wù)邏輯的內(nèi)容卻又分布在各個請求處理器中,所以對于這些內(nèi)容的處理,我們就可以基于AOP的思想將其提取出來(AOP中的切面),使用Filter進行整體設(shè)置。這種方式相當于對類中的內(nèi)容做進一步的抽象,使我們的系統(tǒng)更加靈活,更加能應(yīng)對變化,也進一步提高了代碼復(fù)用。

????此外,F(xiàn)ilter 的實現(xiàn)體現(xiàn)了責任鏈模式的精髓,即將請求的發(fā)送者與請求的處理者解耦,從而使得系統(tǒng)更靈活,也更容易擴展。就像Servlet規(guī)范對Filter描述的那樣,過濾鏈是由Servlet容器提供給開發(fā)者的一種過濾器調(diào)用的視圖,過濾器使用過濾鏈去調(diào)用鏈中的下一個過濾器去處理請求,特別地,如果當前過濾器時過濾鏈中的最后一個過濾器,過濾鏈將把它交給相應(yīng)的資源處理器(Servlet)進行處理。更進一步地說,使用過濾鏈對請求進行過濾的好處就是,發(fā)出請求的客戶端并不知道鏈上的哪一個過濾器將處理這個請求,這使得系統(tǒng)可以在不影響客戶端的情況下,動態(tài)地重新組織鏈和分配責任。并且,在執(zhí)行過程中的任何時候都可以直接返回結(jié)果,也就是說,只要不執(zhí)行 chain.doFilter() 就不會對請求放行,也就不會再執(zhí)行后面的過濾器和請求的內(nèi)容。這顯然可以看作是?非純責任鏈模式?的一種典型實現(xiàn)。

????顯然,FilterChain 本身就是對責任鏈切面的抽象,是對傳統(tǒng)責任鏈模式的一個改進,整個 Filter 機制本身也是AOP思想與責任鏈模式的融合的最佳實踐。

例子:對字符串過濾。

????一個抽象 Filter,三個具體的 Filter,包括 HTMLFilter,SensitiveFilter 和 FaceFilter; FilterChain 用于對處理鏈(責任鏈切面)的抽象。此外,Request 和 Response 用于對請求消息和響應(yīng)消息的抽象,Client 用于對客戶端的抽象,其類圖如下所示:


1.抽象處理者:Filter

publicinterfaceFilter{

????????//每個Filter均為FilterChain的成員, Filter持有FilterChain的引用,以便調(diào)用鏈條中的各處理者

????????void? doFilter(Request request, Response response, FilterChain chain);

}

2、具體處理者:HTMLFilter,SensitiveFilter 和 FaceFilter

// 將請求消息中的"<>"替換成"[]"

public classHTMLFilterimplementsFilter{

?????@Override?

?????public void doFilter(Request request, Response response, FilterChain chain) {

? ? ? ????? // process HTML Tag?

?? ? ? ????String msg = request.getRequest().replace("<", "[").replace(">", "]");

? ? ? ????? request.setRequest(msg);

? ? ? ????? chain.doFilter(request, response);

? ? ? ????? response.setResponse(response.getResponse() + "--->HTMLFilter");

? ? ????}

}


//將請求消息中的"被就業(yè)"替換成"就業(yè)"

class SensitiveFilter implements Filter {

? ????? @Override

? ????? public void doFilter(Request request, Response response, FilterChain chain) {

? ? ? ? ????String msg = request.getRequest().replace("被就業(yè)", "就業(yè)");

? ? ? ????? request.setRequest(msg);

? ? ? ????? chain.doFilter(request, response);

? ? ? ????? response.setResponse(response.getResponse() + "--->SensitiveFilter");

? ????? }

}


// 將請求消息中的":)"替換成"笑臉"

class FaceFilter implements Filter {

? ????? public void doFilter(Request request, Response response, FilterChain chain) {

? ? ? ? ????String msg = request.getRequest().replace(":)", "笑臉");

? ? ? ????? request.setRequest(msg);

? ? ? ? ????chain.doFilter(request, response);

? ? ? ????? response.setResponse(response.getResponse() + "--->FaceFilter");

? ????? }

}


3、過濾鏈的抽象:FilterChain

// 對過濾鏈的抽象(橫切關(guān)注點),是多個過濾器的聚集,本質(zhì)上,F(xiàn)ilterChain 也可以看作是一個大的Filter

public classFilterChain{

? ? ? ? ? ? List filters = new ArrayList();

? ? ? ? ? ?int index = 0;

? ? ? ? ? ?// 鏈式編程?

?????? public FilterChain addFilter(Filter filter){

? ? ? ????? filters.add(filter);

? ? ? ????? return this;? ? // 返回自身? ?

?????????}

? ? public void doFilter(Request request, Response response) {

? ? ? ????? if(index == filters.size()) return;

? ? ? ????? Filter filter = filters.get(index);

? ? ? ????? index++;

? ? ? ????? filter.doFilter(request, response, this);

? ????? }

}

4、請求和響應(yīng)的抽象:Request 和 Response


// 對請求消息的抽象

public class Request{?

?????// 請求消息 private String request;

? ????? public String getRequest() {

? ? ? ????? return request;

? ????? }

? ????? public void setRequest(String request) {

? ? ? ????? this.request = request;

? ????? }

}

// 對響應(yīng)消息的抽象

class Response {

? ????? // 響應(yīng)消息

? ? private String response;

? ? public String getResponse() {

? ? ? ????? return response;

? ? }

? ? public void setResponse(String response) {

? ? ? ????? this.response = response;

? ? }

}

5、客戶端的抽象:Client

public classClient{?

?????public static void main(String[] args) {

? ? ? ????? // 待處理消息

? ? ? ????? String msg = "大家好 :),,敏感,被就業(yè),網(wǎng)絡(luò)授課沒感覺...";

? ? ? ????? // 設(shè)置請求消息?

?? ? ????? Request request = new Request();

? ? ? ????? request.setRequest(msg);

? ? ? ????? // 設(shè)置響應(yīng)消息

? ? ? ????? Response response = new Response();

? ? ? ????? response.setResponse("Response");

? ? ? ????? // 設(shè)置處理鏈

? ? ? ????? FilterChain chain = new FilterChain();

? ? ? ? ????chain.addFilter(new HTMLFilter()).addFilter(new SensitiveFilter())

? ? ? ? ? ? ? ????? .addFilter(new FaceFilter());

? ? ? ????? // 開始處理

? ? ? ????? chain.doFilter(request, response);

? ? ? ????? // 消息的預(yù)處理結(jié)果

? ? ? ????? System.out.println(request.getRequest());

? ? ????? ? // 消息的后處理結(jié)果?

?? ? ????? System.out.println(response.getResponse());

? ????? }

}

/* Output(完全一致):

? ? ? ? 大家好 笑臉,[script],敏感,就業(yè),網(wǎng)絡(luò)授課沒感覺...

? ? ? ? Response--->FaceFilter--->SensitiveFilter--->HTMLFilter

*///:~

????實際上,本示例基本模擬了Java Web 中過濾器的工作流程,也反映了AOP思想和責任鏈模式的精髓。?對于一個給定的請求消息,我們可以從下圖中的方法調(diào)用棧中看出,將依次由 HTMLFilter,SensitiveFilter 和 FaceFilter 三者進行預(yù)處理,最后再依次由 FaceFilter,SensitiveFilter 和 HTMLFilter 處理(這個可以從輸出中看出)。

????實際上,F(xiàn)ilterChain 本身也可以看作是一個大的Filter,更進步地說,F(xiàn)ilterChain 本身也可以實現(xiàn) Filter 接口,這樣做的優(yōu)點是,我們不但可以在客戶端可以任意添加具體的過濾器,還可以添加過濾鏈;但帶來的缺點是將FilterChain 和 Filter 耦合在了一起,也就是說,F(xiàn)ilterChain與Filter的doFilter方法必須一樣,而實際上FilterChain的doFilter方法并不需要FilterChain參數(shù)。花開生兩面,有利就有弊,讀者可以嘗試讓FilterChain 本身實現(xiàn) Filter 接口,體會一下這個思想。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文包括:1、Filter簡介2、Filter是如何實現(xiàn)攔截的?3、Filter開發(fā)入門4、Filter的生命周期...
    廖少少閱讀 7,527評論 3 56
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,688評論 19 139
  • 定義 責任鏈模式是一種對象的行為模式。在責任鏈模式中,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈。請求...
    步積閱讀 2,166評論 1 5
  • 體重下了,脂肪率驟張,好幾個指標一夜間都變差了,無語
    潘潘_ef11閱讀 204評論 0 0
  • 青泥是我今天看到最美的詞了。 現(xiàn)在是2017年7月12日晚上十點三十九分,夜看似好生靜謐,蕊希喃喃在耳畔,電扇也呼...
    子上青黛閱讀 405評論 0 1

友情鏈接更多精彩內(nèi)容