Java設計模式之責任鏈模式

引入責任鏈模式

責任鏈模式

顧名思義,責任鏈模式(Chain of Responsibility Pattern)爲請求建立了一個接收者對象的鏈。這種模式給予請求的類型,對請求的發送者和接收者進行解耦。這種類型的設計模式屬於行爲型模式。在這種模式中,一般每一個接收者都包含對另外一個接收者的引用。若是一個對象不能處理該請求,那麼它會把相同的請求傳給下一個接收者,依此類推。java

介紹

意圖:避免請求發送者與接收者耦合在一塊兒,讓多個對象都有可能接收請求,將這些對象鏈接成一條鏈,而且沿着這條鏈傳遞請求,直到有對象處理它爲止。設計模式

主要解決:職責鏈上的處理者負責處理請求,客戶只須要將請求發送到職責鏈上便可,無須關心請求的處理細節和請求的傳遞,因此職責鏈將請求的發送者和請求的處理者解耦了。數據結構

什麼時候使用:在處理消息的時候以過濾不少道。app

如何解決:攔截的類都實現統一接口。jsp

關鍵代碼:Handler 裏面聚合它本身,在 HanleRequest 裏判斷是否合適,若是沒達到條件則向下傳遞,向誰傳遞以前 set 進去。ide

應用實例: 一、紅樓夢中的"擊鼓傳花"。 二、JS 中的事件冒泡。 三、JAVA WEB 中 Apache Tomcat 對 Encoding 的處理,Struts2 的攔截器,jsp servlet 的 Filter。性能

優勢: 一、下降耦合度。它將請求的發送者和接收者解耦。 二、簡化了對象。使得對象不須要知道鏈的結構。 三、加強給對象指派職責的靈活性。經過改變鏈內的成員或者調動它們的次序,容許動態地新增或者刪除責任。 四、增長新的請求處理類很方便。測試

缺點: 一、不能保證請求必定被接收。 二、系統性能將受到必定影響,並且在進行代碼調試時不太方便,可能會形成循環調用。 三、可能不容易觀察運行時的特徵,有礙於除錯。ui

使用場景: 一、有多個對象能夠處理同一個請求,具體哪一個對象處理該請求由運行時刻自動肯定。 二、在不明確指定接收者的狀況下,向多個對象中的一個提交一個請求。 三、可動態指定一組對象處理請求。this

注意事項:在 JAVA WEB 中遇到不少應用。

Support是一個抽象類,他的核心方法support中,若是當前support能夠解決,就解決,若是不行,就交給next去解決。

package cn.chinotan.service.designpattern.command.ChainOfResponsibility;

/**
 * @program: test
 * @description: 當前節點能夠解決就解決,不然交給下一個節點
 * @author: xingcheng
 * @create: 2018-09-16 16:41
 **/
public abstract class Support {

    /**
     * 處理節點名稱
     */
    private String name;

    /**
     * 下一個處理節點
     */
    private Support next;

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

    public final void support(Problem problem){
        if (resolve(problem)){
            success(problem);
        } else if (next != null){
            next.support(problem);
        } else {
            fail(problem);
        }
    }

    /**
     * 進行解決
     * @param problem 待解決的問題
     * @return
     */
    protected abstract Boolean resolve(Problem problem);

    /**
     * 解決失敗
     * @param problem 待解決的問題
     */
    protected void fail(Problem problem){
        System.out.println(problem + "沒法解決");
    }

    /**
     * 成功解決
     * @param problem 待解決的問題
     */
    protected void success(Problem problem){
        System.out.println(problem + "解決成功} by " + this);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("[name='").append(name).append("]");
        return sb.toString();
    }

    public Support setNext(Support next) {
        this.next = next;
        return next;
    }
}

而後咱們實現幾個具體的support類
NoSupport類是一個永遠不解決問題的類

package cn.chinotan.service.designpattern.command.ChainOfResponsibility;

/**
 * @program: test
 * @description: 永遠不解決問題的節點
 * @author: xingcheng
 * @create: 2018-09-16 17:02
 **/
public class NotSupport extends Support{

    public NotSupport(String name) {
        super(name);
    }

    @Override
    protected Boolean resolve(Problem problem) {
        return false;
    }
}

LimitSupport類,解決指定範圍內的問題

package cn.chinotan.service.designpattern.command.ChainOfResponsibility;

/**
 * @program: test
 * @description: 解決部分範圍的問題
 * @author: xingcheng
 * @create: 2018-09-16 17:15
 **/
public class LimitSupport extends Support {
    
    private Integer limit;
    
    public LimitSupport(Integer limit, String name) {
        super(name);
        this.limit = limit;
    }

    @Override
    protected Boolean resolve(Problem problem) {
        if (problem.getCount() < limit){
            return true;
        } else {
            return false;
        }
    }
}

EvenSupport類,解決偶數的問題

package cn.chinotan.service.designpattern.command.ChainOfResponsibility;

/**
 * @program: test
 * @description: 只解決部分問題的節點
 * @author: xingcheng
 * @create: 2018-09-16 17:02
 **/
public class EvenSupport extends Support {

    public EvenSupport(String name) {
        super(name);
    }

    @Override
    protected Boolean resolve(Problem problem) {
        if ((problem.getCount() & 1) == 0){
            return true;
        } else {
            return false;
        }
    }
}

啓動測試類

package cn.chinotan.service.designpattern.command.ChainOfResponsibility;

/**
 * @program: test
 * @description: 測試啓動類
 * @author: xingcheng
 * @create: 2018-09-16 17:28
 **/
public class DemoTest {

    public static void main(String[] args) {
        NotSupport noSupport = new NotSupport("noSupport");
        LimitSupport limitSupport100 = new LimitSupport(100, "limitSupport100");
        LimitSupport limitSupport200 = new LimitSupport(200, "limitSupport200");
        LimitSupport limitSupport300 = new LimitSupport(300, "limitSupport300");
        EvenSupport evenSupport = new EvenSupport("evenSupport");
        SpecialSupport specialSupport = new SpecialSupport(363, "specialSupport");
        
        noSupport.setNext(limitSupport100).setNext(limitSupport200).setNext(limitSupport300).setNext(evenSupport).setNext(specialSupport);
        
        for (int i = 0; i < 500; i+= 33){
            noSupport.support(new Problem(i));
        }
    }
    
}

Main類中定義了一個責任鏈,將幾個support對象鏈接在一塊兒,組成了一條責任鏈,而後去處理問題
運行結果以下:

責任鏈模式的分析

首先,責任鏈模式中,存在着這麼幾個角色:

  • Handler處理者
    handler金額use定義了處理請求的接口,handler知道,下一個處理者是誰,若是本身沒法處理請求,就轉給下一個處理者。
    在實例中對應的是,support類和support方法

  • concreteHandler(具體的處理者)
    具體的處理者是處理請求的具體角色。
    在此實例中,由NotSupport角色和其餘幾個類扮演

  • Client
    請求者角色,就是向第一個具體的handler發送請求的角色,並鏈接好責任鏈,實例中對應的是main類的main方法。

責任鏈的做用

  • 弱化了發出請求的人和處理請求的人之間的關係
    發出請求的人只須要向第一個具體的處理者發送請求,而後就能夠不用管了,處理者會在責任鏈上本身尋找處理的方法。
    這樣就解耦了處理者和請求者之間的關係。
    若是咱們不採起責任鏈模式,那麼請求者就必需要很清楚哪一個處理者能處理它的請求,就必須對全部的處理者都有所瞭解,相似於上帝視角,然而在實際中,要求請求這瞭解這麼可能是不實際的

  • 能夠動態的改變責任鏈
    責任鏈還有的好處就是能夠動態的改變責任,刪除或者添加或者改變順序。

  • 讓各個處理者專一於實現本身的職責
    責任鏈模式同時還作到了處理者之間的解耦,處理者本身專一於本身的處理邏輯就好,無論其餘處理者幹什麼。

  • 推卸責任也可能致使處理延遲
    咱們能夠責任鏈模式須要在責任鏈上傳播責任,直至找到合適的處理對象。這樣提升了程序的靈活性,但同時也出現了處理的延遲,由於有一個尋找的過程。因此須要低延遲的狀況下,就不該該使用責任鏈模式

責任鏈模式的應用

一、servlet中的Filter,servlet中分別定義了一個 Filter和FilterChain的接口,定義一個Chain,裏面包含了Filter列表和servlet,達到在調用真正servlet以前進行各類filter邏輯

二、Dubbo中的Filter,Dubbo在建立Filter的時候是另一個方法,經過把Filter封裝成 Invoker的匿名類,經過鏈表這樣的數據結構來完成責任鏈,Dubbo的責任鏈就沒有相似FilterChain這樣的類把Filter和調用Invoker結合起來,而是經過建立一個鏈表,調用的時候咱們只知道第一個節點,每一個節點包含了下一個調用的節點信息。 這裏的雖然Invoker封裝Filter沒有顯示的指定next,可是經過java匿名類和final的機制達到一樣的效果

三、Mybatis中的Plugin,Mybatis能夠配置各類Plugin,不管是官方提供的仍是本身定義的,Plugin和Filter相似,就在執行Sql語句的時候作一些操做。Mybatis的責任鏈則是經過動態代理的方式,使用Plugin代理實際的Executor類。(這裏實際還使用了組合模式,由於Plugin能夠嵌套代理)

責任鏈的優勢和缺點

優勢:實現了請求者與處理者代碼分離:發出這個請求的客戶端並不知道鏈上的哪個對象最終處理這個請求,這使得系統能夠在不影響客戶端的狀況下動態地從新組織和分配責任。提升系統的靈活性和可擴展行。

缺點:每次都是從鏈頭開始:這也正是鏈表的缺點。你也許會想到一種貌似不錯的解決方案,好比使用hash映射,將要處理的請求id與處理類對象關聯,可是這樣系統損失了可擴展性。

相關文章
相關標籤/搜索