系統中如果存在多個物件可以處理一個同一請求,可以通過職責鏈模式將這些處理請求的物件連成一條鏈,讓請求沿著該鏈進行傳遞。如果鏈上的物件可以處理該請求則進行處理,否則將請求轉發給下家處理
模式動機
很多情況下,可以處理某個請求的物件不止一個,如大學裡的獎學金審批,學生先向輔導員提交審批表,輔導員簽字審批後再交給系主任簽字審批,接著是院長審批,最後可能是校長審批,在這個過程中,獎學金申請表可以看作一個請求物件,不同級別審批者都可以處理該請求,除了輔導員之外,學生不需一一和其他審批者互動,只需等待結果即可。審批過程中如果某一審批者認為不符合條件,則請求中止,否則將請求傳遞給下一審批者,最後由校長拍板。
在這個過程中,輔導員、系主任、院長、校長構成一條鏈,申請表沿著這條鏈傳遞,就叫職責鏈。職責鏈可以是一條直線、一個環或一個樹形結構,常見的職責鏈是直線型。請求沿著鏈傳遞,由鏈上的處理者對請求進行處理,客戶無須關心請求的處理和傳遞細節,實現請求傳送者和請求處理者解耦。
模式定義
避免請求者與接收者耦合在一起,讓多個物件都有可能接受請求,將這些物件連線成一條鏈,並且沿著這條鏈傳遞請求,直到有物件處理為止。職責鏈模式也叫責任鏈模式,它是一種物件行為型模式。
模式結構
-
Handler(抽象處理者)
定義了一個處理請求的介面,由於不同的具體處理者處理請求方式不同,因此在其中定義抽象請求處理方法。在抽象處理者中定義一個自型別(抽象處理者型別)的物件,作為對下家的引用。
-
ConcreteHandler(具體處理者)
具體處理者是抽象處理者子類,實現抽象請求處理方法,處理使用者請求。在處理請求之前要進行判斷,看是否具有相應處理許可權,如果可以處理就處理,否則將請求轉發給後繼者。
-
Client(客戶類)
用於向鏈中物件提出最初的請求,客戶類只關心鏈的源頭,而無須關心請求的處理細節和傳遞過程。
模式例項
某系統提供一個假條審批的模組,如果員工請假天數小於 3 天,主任可以審批該假條;如果員工請假天數大於等於 3 天,小於 10 天,經理可以審批;如果員工請假天數大於等於 10 天,小於 30 天,總經理可以審批;如果超過 30 天,總經理也不能審批,提示相應的拒絕資訊。
-
請求類 LeaveRequest(請假條類)
// 封裝請求的相關資訊,以便處理者對其進行處理 public class LeaveRequest { private String leaveName; private int leaveDays; public LeaveRequest(String leaveName, int leaveDays) { this.leaveName = leaveName; this.leaveDays = leaveDays; } public void setLeaveName(String leaveName) { this.leaveName = leaveName; } public void setLeaveDays(int leaveDays) { this.leaveDays = leaveDays; } public String getLeaveName() { return leaveName; } public int getLeaveDays() { return leaveDays; } }
-
抽象處理類 Leader(領導類)
public abstract class Leader { protected String name; protected Leader successor; // 作為對下家的引用 public Leader(String name) { this.name = name; } public void setSuccessor(Leader successor) { this.successor = successor; } public abstract void handleRequest(LeaveRequest request); }
-
具體處理者 Director(主任類)
public class Director extends Leader { public Director(String name) { super(name); } @Override public void handleRequest(LeaveRequest request) { if (request.getLeaveDays() < 3) { System.out.println("主任" + name + "審批員工" + request.getLeaveName() + "的請假條,請假天數為" + request.getLeaveDays() + "天"); } else { if (this.successor != null) { this.successor.handleRequest(request); } } } }
-
具體處理者 Manager(經理類)
public class Manager extends Leader { public Manager(String name) { super(name); } @Override public void handleRequest(LeaveRequest request) { if (request.getLeaveDays() < 10) { System.out.println("經理" + name + "審批員工" + request.getLeaveName() + "的請假條,請假天數為" + request.getLeaveDays() + "天"); } else { if (this.successor != null) { this.successor.handleRequest(request); } } } }
-
具體處理者 GeneralManager(總經理類)
public class GeneralManager extends Leader { public GeneralManager(String name) { super(name); } @Override public void handleRequest(LeaveRequest request) { if (request.getLeaveDays() < 30) { System.out.println("總經理" + name + "審批員工" + request.getLeaveName() + "的請假條,請假天數為" + request.getLeaveDays() + "天"); } else { System.out.println("想請假" + request.getLeaveDays() + "天?你是不想幹了吧!"); } } }
-
客戶端測試類 Client
public class Client { public static void main(String[] args) { Leader director = new Director("王明"); Leader manager = new Manager("趙強"); Leader generalManager = new GeneralManager("陳勇"); director.setSuccessor(manager); manager.setSuccessor(generalManager); LeaveRequest lr1 = new LeaveRequest("張三", 2); director.handleRequest(lr1); LeaveRequest lr2 = new LeaveRequest("李四", 5); director.handleRequest(lr2); LeaveRequest lr3 = new LeaveRequest("王五", 15); director.handleRequest(lr3); LeaveRequest lr4 = new LeaveRequest("趙六", 45); director.handleRequest(lr4); } }
-
執行結果
模式優缺點
職責鏈模式優點如下:
- 降低耦合度。物件不需知道是誰處理請求,之需等待處理結果即可。接收者和傳送則都沒有對方的明確資訊,且鏈中的物件不需要知道鏈的結構,由客戶端負責鏈的建立
- 簡化物件的相互連線。請求處理物件僅需維持一個指向其後繼者的引用,而不需維持對所有候選處理者的引用
- 增強給物件指派職責的靈活性。可以通過在執行時對該鏈進行動態的增加或修改來增加或改變一個請求
- 增加新的請求處理類很方便。增加一個新的具體請求處理者無須修改原始碼,只需在客戶端重新建鏈即可,符合開閉原則
職責鏈模式缺點如下:
- 不能保證請求一定被接收。該請求可能一直到末端都得不到處理,也有可能沒有被正確配置而得不到處理
- 對於較長的職責鏈,請求的處理可能涉及多個處理物件,系統效能將受到一定影響,進行程式碼調式也不方便
模式適用環境
以下情況可以考慮使用職責鏈模式:
- 有多個物件可以處理同一請求,具體哪個物件處理由執行時刻決定
- 在不明確指定接收者的情況下,向多個物件中的一個提交請求。請求沿著鏈進行傳遞,尋找相應的處理者
- 動態指定一組物件處理請求,客戶端可以動態建立職責鏈來處理請求,還可以動態改變鏈中處理者之間的先後次序
純與不純的職責鏈模式
一個純的職責鏈模式要求某一處理者物件要麼接收請求,承擔責任,要麼把責任推給下家。在一個不純的職責鏈模式裡面,一個請求可以被由某一處理物件承擔一部分責任後,又將責任往下傳,或者最終不被任何接收端物件所接收。實際中我們接觸到的大多是不純的職責鏈模式。