1. 程式人生 > 實用技巧 >設計模式學習總結:責任鏈模式

設計模式學習總結:責任鏈模式

​ 本文為筆者在閱讀一些書籍、部落格、專欄等資料後所總結的個人對於責任鏈模式的筆記,由於筆者才疏學淺,若有不足之處,還望各位加以斧正,您的建議與鼓勵都是筆者源源不斷的前進動力。感謝!

文章大綱如下:

  • 責任鏈模式的簡單認識
  • 責任鏈模式的簡單應用
    • 兩種責任鏈的實現方法:基於陣列、連結串列
    • 責任鏈的一些簡單應用
  • 責任鏈模式在原始碼中的體現
  • 責任鏈模式的優缺點

責任鏈模式簡單認識

Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. ---- 《設計模式》GoF

中文意思為:

通過給多個物件處理請求的機會,避免請求的傳送方與其接收方耦合。將接收物件串起來,並沿著鏈傳遞請求,直到有一個物件處理它。

筆者的將其理解為:

現在有一條責任鏈(其中包含著各種接收物件,也可以理解為處理器,其具有處理請求的方法)。傳送方對責任鏈傳送請求,責任鏈中的物件A接收到請求後,若其能處理該請求,則處理,否則就向下一個物件傳遞請求,直到有一個物件能對其進行處理。整個過程形成一條鏈。

此外,責任鏈模式還有另一種實現方式,即

請求進入責任鏈,物件A接收到請求後,若需要處理,則處理,處理後傳遞給下一個物件;若不需要處理,則直接傳遞給下一個物件。也就是說,其最終都會將該物件傳遞給鏈上的下一個物件,以此類推,直到最後一個物件處理後,方結束。此變體中的請求會被責任鏈中的每個處理器(物件)處理。

責任鏈模式的簡單應用

這兩種責任鏈的實現方法

以下實現程式碼參考自王爭先生在極客時間上的《設計模式之美》所展示的程式碼片段。我在其中註釋部分加入了自己的理解、總結。

第一種責任鏈(即處理後就返回)

此責任鏈的實現方法有兩種,分別是陣列實現和連結串列實現。

  • 陣列實現

陣列實現的大致模板:

/**
 * 處理者介面,定義了處理者的行為
 */
interface IHandler {
    boolean handle();
}

/**
 * 處理者A
 */
class HandlerA implements IHandler  {
    @Override
    public boolean handle() {
        boolean status = true;
        // 此處省略處理任務的方法……
        // 如果任務被處理了,就令status為true;否則令其為false
        System.out.println("[HandlerA]: status is " + status);
        return status;
    }
}

/**
 * 處理者B
 */
class HandlerB implements IHandler  {
    @Override
    public boolean handle() {
        boolean status = true;
        // 此處省略處理任務的方法……
        // 如果任務被處理了,就令status為true;否則令其為false
        System.out.println("[HandlerB]: status is " + status);
        return status;
    }
}

/**
 * 責任鏈
 */
class HandlerChain {
    // 責任鏈的陣列儲存物件
    private List<IHandler> handlerList = new ArrayList<>();
    
    // 向陣列中新增handler的方法,新增成功返回true,反之為false
    public boolean addHandler(IHandler handler) {
        return this.handlerList.add(handler);
    }
    
    // 責任鏈呼叫處理者去處理請求
    public void handle() {
        // 遍歷責任鏈上的處理者
        for (IHandler handler : handlerList) {
            boolean status = handler.handle();
            // 如果處理者返回的是true,就說明已經處理請求了,可以退出遍歷了
            // 反之,繼續遍歷,直到被處理
            if (status == true) {
                break;
            }
        }
    }
}

/**
 * 基於陣列實現的責任鏈(處理後就停止的責任鏈)
 */
public class Demo {
    public static void main(String[] args) {
        HandlerChain handlerChain = new HandlerChain();
        handlerChain.addHandler(new HandlerA());
        handlerChain.addHandler(new HandlerB());
        handlerChain.handle();
    }
}

輸出結果為:

可見,HandleA處理了請求後,就不再往後傳遞請求了。

陣列實現的場景應用:

/**
 * 先留個坑...有時間了來填...
 */
  • 連結串列實現

連結串列實現的大致模板:

/**
 * 處理者抽象類,定義了處理者的行為
 */
abstract class IHandler {
    // 下一個處理者
    protected IHandler nextHandler;

    // 子類需要實現的處理方法
    protected abstract boolean doHandle();

    // 設定下一個處理者
    public void setNextHandler(IHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    // 獲取下一個處理者
    public IHandler getNextHandler() {
        return this.nextHandler;
    }

    // 父類呼叫子類的處理方法判斷是否向後傳遞請求
    public final void handle() {
        boolean status = doHandle();
        if ((!status) && (this.nextHandler != null)) {
            this.nextHandler.handle();
        }
    }
}

/**
 * 處理者A
 */
class HandlerA extends IHandler  {
    @Override
    public boolean doHandle() {
        boolean status = false;
        // 此處省略處理任務的方法……
        // 如果任務被處理了,就令status為true;否則令其為false
        System.out.println("[HandlerA]: status is " + status);
        return status;
    }
}

/**
 * 處理者B
 */
class HandlerB extends IHandler  {
    @Override
    public boolean doHandle() {
        boolean status = true;
        // 此處省略處理任務的方法……
        // 如果任務被處理了,就令status為true;否則令其為false
        System.out.println("[HandlerB]: status is " + status);
        return status;
    }
}

/**
 * 責任鏈
 */
class HandlerChain {
    // 責任鏈的頭結點
    private IHandler headHandler;
    // 責任鏈的尾結點
    private IHandler tailHandler;

    // 向責任鏈中新增處理者
    public void addHandler(IHandler handler) {
        // 把處理者的下一個結點設為空
        handler.setNextHandler(null);
        // 如果頭結點為空,說明責任鏈現在沒有任何處理者,此時把頭結點和尾結點都設定為函式傳入的IHandler例項
        if (this.headHandler == null) {
            this.headHandler = handler;
            this.tailHandler = handler;
            return; // 設定好後就返回,因為後面要給head非空時的情況賦值
        }
        // headHandler非空時,把傳進來的結點加入到連結串列尾部
        this.tailHandler.setNextHandler(handler); 
        this.tailHandler = handler;
    }

    // 責任鏈呼叫處理者去處理請求
    public void handle() {
        if (this.headHandler != null) {
            this.headHandler.handle();
        }
    }
}

/**
 * 基於連結串列實現的責任鏈(處理後就停止的責任鏈)
 */
public class Demo {
    public static void main(String[] args) {
        HandlerChain handlerChain = new HandlerChain();
        handlerChain.addHandler(new HandlerA());
        handlerChain.addHandler(new HandlerB());
        handlerChain.handle();
    }
}

輸出結果:

連結串列實現的場景應用:

/**
 * 先留個坑...有時間了來填...
 */
第二種責任鏈(一直往後傳遞)

此責任鏈的實現方法也是兩種,即陣列、連結串列實現。

  • 陣列實現

陣列實現的大致模板:

/**
 * 處理者介面,定義了處理者的行為
 */
interface IHandler {
    // 實現類的處理方法
    void handle();
}

/**
 * 處理者A
 */
class HandlerA implements IHandler {
    @Override
    public void handle() {
        // 此處省略處理任務的方法…
        System.out.println("[HandlerA]: handle()");
    }
}

/**
 * 處理者B
 */
class HandlerB implements IHandler {
    @Override
    public void handle() {
        // 此處省略處理任務的方法……
        System.out.println("[HandlerB]: handle()");
    }
}

/**
 * 責任鏈
 */
class HandlerChain {
    // 責任鏈的儲存物件
    List<IHandler> handlerList = new ArrayList<>();

    // 向責任鏈中新增處理者
    public HandlerChain addHandler(IHandler handler) {
        handlerList.add(handler);
        return this;
    }

    // 責任鏈呼叫處理者去處理請求
    public void handle() {
        for (IHandler handler : handlerList) {
            handler.handle();
        }
    }
}

/**
 * 基於陣列實現的責任鏈(處理後就停止的責任鏈)
 */
public class Demo {
    public static void main(String[] args) {
        HandlerChain handlerChain = new HandlerChain();
        IHandler handlerA = new HandlerA();
        IHandler handlerB = new HandlerB();
        handlerChain.addHandler(handlerA).addHandler(handlerB);
        handlerChain.handle();
    }
}

輸出結果:

陣列實現的場景應用:

/**
 * 先留個坑...有時間了來填...
 */
  • 連結串列實現

連結串列實現的大致模板:

/**
 * 處理者抽象類,定義了處理者的行為
 */
abstract class IHandler {
    // 下一個處理者
    protected IHandler nextHandler;

    // 子類需要實現的處理方法
    protected abstract void doHandle();

    // 設定下一個處理者
    public void setNextHandler(IHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    // 獲取下一個處理者
    public IHandler getNextHandler() {
        return this.nextHandler;
    }

    // 父類呼叫子類的處理方法並向後請求
    public final void handle() {
        doHandle();
        // 與前一種責任鏈所不同的是,這裡不需要通過前面是否處理判斷是否傳遞,而是一直傳遞
        if (this.nextHandler != null) {
            System.out.println("[IHandler]-nextHandler: " + this.nextHandler.getClass());
            this.nextHandler.handle();
        }
    }
}

/**
 * 處理者A
 */
class HandlerA extends IHandler  {
    @Override
    public void doHandle() {
        // 此處省略處理任務的方法…
        System.out.println("[HandlerA]: doHandler()");
    }
}

/**
 * 處理者B
 */
class HandlerB extends IHandler  {
    @Override
    public void doHandle() {
        // 此處省略處理任務的方法……
        System.out.println("[HandlerB]: doHandler()");
    }
}

/**
 * 責任鏈
 */
class HandlerChain {
    // 責任鏈的頭結點
    private IHandler headHandler;
    // 責任鏈的尾結點
    private IHandler tailHandler;

    // 向責任鏈中新增處理者
    public void addHandler(IHandler handler) {
        // 把處理者的下一個結點設為空
        handler.setNextHandler(null);
        // 如果頭結點為空,說明責任鏈現在沒有任何處理者,此時把頭結點和尾結點都設定為函式傳入的IHandler例項
        if (this.headHandler == null) {
            this.headHandler = handler;
            this.tailHandler = handler;
            return; // 設定好後就返回,因為後面要給head非空時的情況賦值
        }
        // headHandler非空時,把傳進來的結點加入到連結串列尾部
        this.tailHandler.setNextHandler(handler);
        this.tailHandler = handler;
    }

    // 責任鏈呼叫處理者去處理請求
    public void handle() {
        if (this.headHandler != null) {
            this.headHandler.handle();
        }
    }
}

/**
 * 基於連結串列實現的責任鏈(處理後就停止的責任鏈)
 */
public class Demo {
    public static void main(String[] args) {
        HandlerChain handlerChain = new HandlerChain();
        handlerChain.addHandler(new HandlerA());
        handlerChain.addHandler(new HandlerB());
        handlerChain.handle();
    }
}

輸出結果:

可以看到,HandlerA在處理了請求後,又傳遞給了HandlerB,HandlerB也對請求進行了處理。

連結串列實現的場景應用:

/**
 * 先留個坑...有時間了來填...
 */

責任鏈模式在原始碼中的體現

/**
 * 先留個坑...有時間了來填...
 */

責任鏈模式的優缺點

最後,總結下責任鏈模式的優缺點。

優點
  • 將請求的傳送者和接收者解耦。
  • 簡化物件,減少程式碼的複雜性。(處理者不需要知道責任鏈的內部構造以及如何處理的)
  • 滿足開閉原則,提高程式碼的擴充套件性。(動態地新增或刪除責任)
缺點
  • 不容易觀察執行時的特徵,出現問題不好排查。(如果某一節點出現問題,則容易造成系統崩潰)
  • 並不一定保證請求一定會執行。(責任鏈過長時,請求沒被處理或者處理時間過長,會影響整體效能)