責任鏈模式

設計模式文章

中介者模式

代理模式

抽象工廠模式詳解 —— head first 設計模式

裝飾者模式

適配器模式

策略模式

觀察者模式

建造者模式 (Builder)

概述

顧名思義,責任鏈模式(Chain of Responsibility Pattern)為請求創建了一個接收者對象的鏈。這種模式給予請求的類型,對請求的發送者和接收者進行解耦。這種類型的設計模式屬於行為型模式。

在這種模式中,通常每個接收者都包含對另一個接收者的引用。如果一個對象不能處理該請求,那麼它會把相同的請求傳給下一個接收者,依此類推。

在現實生活中,常常會出現這樣的事例:一個請求有多個對象可以處理,但每個對象的處理條件或許可權不同。例如,公司員工請假,可批假的領導有部門負責人、副總經理、總經理等,但每個領導能批准的天數不同,員工必須根據自己要請假的天數去找不同的領導簽名,也就是說員工必須記住每個領導的姓名、電話和地址等資訊,這增加了難度。這樣的例子還有很多,如找領導出差報銷、生活中的「擊鼓傳花」遊戲等。

在電腦軟硬體中也有相關例子,如匯流排網中數據報傳送,每台電腦根據目標地址是否同自己的地址相同來決定是否接收;還有異常處理中,處理程式根據異常的類型決定自己是否處理該異常;還有 Struts2 的攔截器、JSP 和 Servlet 的 Filter 等,所有這些,如果用責任鏈模式都能很好解決。

模式的定義與特點

責任鏈(Chain of Responsibility)模式的定義:為了避免請求發送者與多個請求處理者耦合在一起,將所有請求的處理者通過前一對象記住其下一個對象的引用而連成一條鏈;當有請求發生時,可將請求沿著這條鏈傳遞,直到有對象處理它為止。

注意:責任鏈模式也叫職責鏈模式。

在責任鏈模式中,客戶只需要將請求發送到責任鏈上即可,無須關心請求的處理細節和請求的傳遞過程,所以責任鏈將請求的發送者和請求的處理者解耦了。

責任鏈模式是一種對象行為型模式,其主要優點如下。

  1. 降低了對象之間的耦合度。該模式使得一個對象無須知道到底是哪一個對象處理其請求以及鏈的結構,發送者和接收者也無須擁有對方的明確資訊。

  2. 增強了系統的可擴展性。可以根據需要增加新的請求處理類,滿足開閉原則。

  3. 增強了給對象指派職責的靈活性。當工作流程發生變化,可以動態地改變鏈內的成員或者調動它們的次序,也可動態地新增或者刪除責任。

  4. 責任鏈簡化了對象之間的連接。每個對象只需保持一個指向其後繼者的引用,不需保持其他所有處理者的引用,這避免了使用眾多的 if 或者 if···else 語句。

  5. 責任分擔。每個類只需要處理自己該處理的工作,不該處理的傳遞給下一個對象完成,明確各類的責任範圍,符合類的單一職責原則。

其主要缺點如下。

  1. 不能保證每個請求一定被處理。由於一個請求沒有明確的接收者,所以不能保證它一定會被處理,該請求可能一直傳到鏈的末端都得不到處理。

  2. 對比較長的職責鏈,請求的處理可能涉及多個處理對象,系統性能將受到一定影響。

  3. 職責鏈建立的合理性要靠客戶端來保證,增加了客戶端的複雜性,可能會由於職責鏈的錯誤設置而導致系統出錯,如可能會造成循環調用。

模式的結構與實現

通常情況下,可以通過數據鏈表來實現職責鏈模式的數據結構

1. 模式的結構

職責鏈模式主要包含以下角色。

  1. 抽象處理者(Handler)角色:定義一個處理請求的介面,包含抽象處理方法和一個後繼連接。

  2. 具體處理者(Concrete Handler)角色:實現抽象處理者的處理方法,判斷能否處理本次請求,如果可以處理請求則處理,否則將該請求轉給它的後繼者。

  3. 客戶類(Client)角色:創建處理鏈,並向鏈頭的具體處理者對象提交請求,它不關心處理細節和請求的傳遞過程。

其結構圖如圖 1 所示。客戶端可按圖 2 所示設置責任鏈。

責任鏈模式的結構圖
圖1 責任鏈模式的結構圖

責任鏈
圖2 責任鏈

2. 模式的實現

職責鏈模式的實現程式碼如下:

package chainOfResponsibility;
public class ChainOfResponsibilityPattern {
    public static void main(String[] args) {
        //組裝責任鏈 
        Handler handler1=new ConcreteHandler1(); 
        Handler handler2=new ConcreteHandler2(); 
        handler1.setNext(handler2); 
        //提交請求 
        handler1.handleRequest("two");
    }
}
//抽象處理者角色
abstract class Handler {
    private Handler next;
  // 用來設置下一個處理者
public void setNext(Handler next) { this.next=next; } public Handler getNext() { return next; } //處理請求的方法 public abstract void handleRequest(String request); } //具體處理者角色1 class ConcreteHandler1 extends Handler { public void handleRequest(String request) { if(request.equals("one")) { System.out.println("具體處理者1負責處理該請求!"); } else { if(getNext()!=null) { getNext().handleRequest(request); } else { System.out.println("沒有人處理該請求!"); } } } } //具體處理者角色2 class ConcreteHandler2 extends Handler { public void handleRequest(String request) { if(request.equals("two")) { System.out.println("具體處理者2負責處理該請求!"); } else { if(getNext()!=null) { getNext().handleRequest(request); } else { System.out.println("沒有人處理該請求!"); } } } }

模式的應用場景

前邊已經講述了關於責任鏈模式的結構與特點,下面介紹其應用場景,責任鏈模式通常在以下幾種情況使用。

  1. 有多個對象可以處理一個請求,哪個對象處理該請求由運行時刻自動確定。

  2. 可動態指定一組對象處理請求,或添加新的處理者。

  3. 在不明確指定請求處理者的情況下,向多個處理者中的一個提交請求。

模式的擴展

職責鏈模式存在以下兩種情況。

  1. 純的職責鏈模式:一個請求必須被某一個處理者對象所接收,且一個具體處理者對某個請求的處理只能採用以下兩種行為之一:自己處理(承擔責任);把責任推給下家處理。

  2. 不純的職責鏈模式:允許出現某一個具體處理者對象在承擔了請求的一部分責任後又將剩餘的責任傳給下家的情況,且一個請求可以最終不被任何接收端對象所接收。