責(zé)任鏈模式

定義

使多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求,從而避免了請(qǐng)求的發(fā)送者和接收者之間的耦合關(guān)系。將這些對(duì)象連成一條鏈,并沿著這條鏈傳遞該請(qǐng)求,直到有對(duì)象處理它為止

使用場(chǎng)景

如果一個(gè)請(qǐng)求可能會(huì)出現(xiàn)多個(gè)或未知個(gè)數(shù)處理器實(shí)例,或者請(qǐng)求處理器可動(dòng)態(tài)配置的情況下,這時(shí)候便可使用責(zé)任鏈模式。
幾乎所有的開(kāi)源框架中都使用到該模式,如 Spring 中的攔截器、過(guò)濾器,通過(guò) ant 表達(dá)式風(fēng)格的 url 參數(shù)來(lái)判斷是否對(duì)請(qǐng)求進(jìn)行攔截,Netty 中 ChannelPipeLine 通過(guò)鏈表的形式添加 ChannelHandler 處理節(jié)點(diǎn)。

在SpringBoot中使用

結(jié)構(gòu)圖

chain.jpg

責(zé)任鏈的處理核心在"鏈"(Chain)上面。"鏈"是由多個(gè)處理者 ConcreateX 組成的,我們先來(lái)看抽象 “Handler” 接口:

/**
 * @author tianp
 **/
public interface Handler {
    /**
     * 是否支持處理
     *
     * @param uri 匹配uri
     * @return true 成功 false 失敗
     */
    boolean support(String uri);

    /**
     * 處理
     *
     * @param requestBody 處理參數(shù)封裝
     * @return true 成功 false 失敗
     */
    boolean handle(RequestBody requestBody);

    /**
     * 獲取處理器名字
     *
     * @return 處理器名字
     */
    String getName();

}

RequestBody 對(duì)請(qǐng)求的參數(shù)進(jìn)行封裝


/**
 * 請(qǐng)求處理封裝類
 *
 * @author tianp
 **/
public class RequestBody {
    private String uri;
    //根據(jù)業(yè)務(wù)定義
    private Object body;

    public String getUri() {
        return uri;
    }

    public void setUri(String uri) {
        this.uri = uri;
    }

    public Object getBody() {
        return body;
    }

    public void setBody(Object body) {
        this.body = body;
    }
}

處理器接口 “Hanlder” 定義了三個(gè)標(biāo)準(zhǔn):

  • 是否支持處理。通過(guò)參數(shù) uri 判斷當(dāng)前 “Handler” 是否支持處理, 如果當(dāng)前不能處理就傳遞給下一個(gè) “Handler” 處理
  • 真正進(jìn)行處理的方法。傳遞 RequestBody給 handle 方法,處理成功則傳遞下一個(gè),不成功則返回false,結(jié)束。
  • 獲取當(dāng)前處理器名字。便于打印日志排查問(wèn)題

/**
 * @author tianp
 **/
public abstract class AbstractHandler implements Handler {
    /**
     * 處理器名稱
     */
    private String name;
    /**
     * 攔截uri
     */
    private String[] includePatterns;

    /**
     * 放行uri
     */
    private String[] excludePatterns;

    /**
     * ant 匹配
     */
    private PathMatcher pathMatcher = new AntPathMatcher();
    /**
     * 下一個(gè)處理器
     */
    private AbstractHandler next = null;

    public AbstractHandler(String name) {
        this.name = name;
    }

    public boolean support(String uri) {
        if (excludePatterns != null) {
            for (String exclude : excludePatterns) {
                if (pathMatcher.match(exclude, uri)) {
                    return false;
                }
            }
        }
        if (includePatterns != null) {
            for (String include : includePatterns) {
                if (pathMatcher.match(include, uri)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 給子類實(shí)現(xiàn)的真正處理方法
     *
     * @param requestBody 請(qǐng)求參數(shù)
     * @return true 成功 false 失敗
     */
    public abstract boolean process(RequestBody requestBody);

    public boolean handle(RequestBody requestBody) {
        if (next != null) {
            if (next.support(requestBody.getUri())) {
                System.out.println(next.getName() + "開(kāi)始處理");
                return next.process(requestBody);
            } else {
                next.handle(requestBody);
            }
        }
        return true;
    }
}
//.... 省略 get/set 方法

抽象處理器 AbstractHandler 實(shí)現(xiàn)了 Handler 接口,實(shí)現(xiàn)了對(duì)應(yīng)的

  • support() 通過(guò) AntPathMatcher 使用 ant 風(fēng)格的表達(dá)式來(lái)匹配當(dāng)前類是否支持?jǐn)r截
  • handle() 通過(guò)持有一個(gè) next 指針來(lái)進(jìn)行請(qǐng)求傳遞
  • getName()
  • process() 給子類實(shí)現(xiàn)的真正處理的方法
/**
 * @author tianp
 **/
public class LinkedHandlerChain {
    /**
     * 頭節(jié)點(diǎn)
     */
    private static final AbstractHandler HEAD = new AbstractHandler("head"){

        @Override
        public boolean process(RequestBody requestBody) {
            return true;
        }
    };
    private AbstractHandler TAIL = HEAD;
    public void addLast(AbstractHandler handler){
        TAIL.setNext(handler);
        TAIL = handler;
    }
    public boolean handle(RequestBody requestBody){
       return HEAD.handle(requestBody);
    }
}

最后通過(guò)一個(gè)鏈 LinkedhandlerChain 串起來(lái)。通過(guò)持有鏈表的指針 HEAD、TAIL 來(lái)處理和添加節(jié)點(diǎn)。

優(yōu)點(diǎn)

非常顯著的優(yōu)點(diǎn)就是將請(qǐng)求和處理分開(kāi)。請(qǐng)求者不用知道是誰(shuí)處理的,處理者不用知道請(qǐng)求的全貌。兩者解耦,提高系統(tǒng)靈活性

缺點(diǎn)

  1. 性能問(wèn)題。每個(gè)請(qǐng)求都是從鏈頭遍歷到鏈尾,特別是在鏈比較長(zhǎng)的時(shí)候,性能是一個(gè)非常大的問(wèn)題。
  2. 調(diào)試不方便。當(dāng)鏈比較長(zhǎng)、環(huán)節(jié)比較多的時(shí)候,由于采取了類似遞歸的方式,調(diào)試的時(shí)候邏輯可能比較復(fù)雜

最佳實(shí)踐

責(zé)任鏈在實(shí)際項(xiàng)目中,曾經(jīng)在項(xiàng)目中使用它來(lái)對(duì)一部分功能進(jìn)行鑒權(quán),因?yàn)楫?dāng)時(shí)只有一部分接口需要鑒權(quán),如果引入第三方框架如:Spring Security 和 Shiro 不僅增加項(xiàng)目的復(fù)雜度,還讓項(xiàng)目變 “重”,因此通過(guò)引入 責(zé)任鏈模式,我可以很好的解決這個(gè)問(wèn)題

更多參考

代碼倉(cāng)庫(kù)地址:https://github.com/To-echo/chain-design/tree/master

參考書(shū)籍:《設(shè)計(jì)模式之禪》

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

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

  • 1.通過(guò)okhttp的核心思想構(gòu)建基本網(wǎng)絡(luò)請(qǐng)求庫(kù),具體pptx在以下鏈接: 鏈接:https://pan.baid...
    曾是個(gè)文物閱讀 996評(píng)論 0 0
  • 1.初識(shí)職責(zé)鏈模式 使多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求,從而避免請(qǐng)求的發(fā)送者和接收者之間的耦合關(guān)系。將這些對(duì)象連成一條鏈,...
    王偵閱讀 938評(píng)論 0 1
  • 責(zé)任鏈模式CoR (Chain of Responsibility) 概述 責(zé)任鏈模式是一種設(shè)計(jì)模式。在責(zé)任鏈模式...
    n油炸小朋友閱讀 2,183評(píng)論 0 4
  • 目錄 本文的結(jié)構(gòu)如下: 引言 什么是責(zé)任鏈模式 模式的結(jié)構(gòu) 典型代碼 代碼示例 純與不純的責(zé)任鏈模式 優(yōu)點(diǎn)和缺點(diǎn) ...
    w1992wishes閱讀 1,322評(píng)論 4 14
  • 到了屬于自己的季節(jié),花兒便換了主人的模樣,好客的主人從不吝嗇,她大方端出所有美味佳肴招待來(lái)訪者,不用爭(zhēng)搶,來(lái)者不拒...
    文心訪藝閱讀 441評(píng)論 0 2

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