Java設計模式-責任鏈模式
責任鏈模式: 將能夠處理某一類請求的物件串成一條鏈, 請求沿鏈傳遞, 鏈上的物件逐個判斷是否有能力處理該請求. 使多個物件都有機會處理請求, 從而避免請求傳送者和接收者之間的耦合關係.
(圖片來源: 設計模式: 可複用面向物件軟體的基礎)
優勢: 發出請求的客戶端並不知道鏈上的哪個物件最終處理該請求, 這使得系統可以在不影響客戶端的前提下動態地重新組織和分配責任.
模式實現
案例: 僱員要求 (請假 & 漲薪), 要經過總監Director -> 經理Manager -> 總經理GeneralManager的層層審批.
Handler:
定義一個處理請求的介面, 內持繼任者(可選):
public abstract class Leader {
protected Leader superior;
protected String name;
protected Leader(Leader superior, String name) {
this.superior = superior;
this.name = name;
}
public abstract void handle(Request request);
}
ConcreteHandler
處理它所負責的請求, 可訪問它的後繼者;
如果可處理該請求, 處理之, 否則將請求轉發:
// 總監 class Director extends Leader { public Director(Leader superior, String name) { super(superior, name); } @Override public void handle(Request request) { if (request.getType().equals("請假") && request.getCount() <= 10) { System.out.println("[ " + request.getContent() + "] 請假 [" + request.getCount() + "]天, 總監 [" + name + "] 審批通過"); } else { if (superior != null) { superior.handle(request); } } } } // 經理 class Manager extends Leader { public Manager(Leader superior, String name) { super(superior, name); } @Override public void handle(Request request) { if (request.getType().equals("請假") && request.getCount() <= 20) { System.out.println("[ " + request.getContent() + "] 請假 [" + request.getCount() + "]天, 經理 [" + name + "] 審批通過"); } else if (request.getType().equals("漲薪") && request.getCount() <= 1000) { System.out.println("[ " + request.getContent() + "] 漲薪 [" + request.getCount() + "]RMB, 經理 [" + name + "] 審批通過"); } else { if (superior != null) { superior.handle(request); } } } } // 總經理 class GeneralManager extends Leader { public GeneralManager(Leader superior, String name) { super(superior, name); } @Override public void handle(Request request) { if (request.getType().equals("請假")) { if (request.getCount() <= 30) { System.out.println("[ " + request.getContent() + "] 請假 [" + request.getCount() + "]天, 總經理 [" + name + "] 審批通過"); } else { System.out.println("[ " + request.getContent() + "] 你乾脆辭職算了"); } } else if (request.getType().equals("漲薪")) { if (request.getCount() <= 10_000) { System.out.println("[ " + request.getContent() + "] 漲薪 [" + request.getCount() + "]RMB, 總經理 [" + name + "] 審批通過"); } else { System.out.println("你咋不上天呢"); } } } }
Client
向鏈上的具體處理者物件提交請求:
public class Client {
@Test
public void client() {
Leader generalManger = new GeneralManager(null, "劉備");
Leader manager = new Manager(generalManger, "諸葛亮");
Leader director = new Director(manager, "趙雲");
director.handle(new Request("請假", "翡青", 32));
director.handle(new Request("漲薪", "zjf", 1500));
}
}
public class Request {
private String type;
private String content;
private int count;
public Request() {
}
public Request(String type, String content, int count) {
this.type = type;
this.content = content;
this.count = count;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
注: 非連結串列實現責任鏈 - 還可通過集合、陣列等形式儲存責任鏈, 很多專案中, 每個具體的Handler並不是開發團隊定義的, 而是專案上線後又外部單位追加的, 此時使用連結串列方式定義chain of responsibility就很困難, 此時可選擇使用集合儲存.
小結
優缺點
降低耦合度: 客戶提交一個請求, 請求沿鏈傳遞直至一個ConcreteHandler最終處理, 接收者和傳送者都沒有對方的明確資訊, 便於接受者與傳送者的解耦.
增強給物件指派職責的靈活性: 鏈中物件自己並不清楚鏈結構,他們僅保持一個後繼者指標, 因此責任鏈可簡化物件的相互連線, 且可以隨時增加或修改處理請求的物件, 增強了給物件指派職責的靈活性.
缺陷: 不保證被接受: 既然一個請求沒有明確的接收者, 那麼就不能保證它能一定被正確處理, 即一個請求有可能到了鏈的末端也得不到處理, 或因為沒有正確配置鏈順序而得不到“正確”處理.
場景
有多個物件可以處理一類請求, 且哪個物件處理由執行時刻自動確定;
在不明確指定接收者的情況下, 向多個物件中提交同一個請求;
處理一個請求的物件集合被動態指定;
Java異常機制: 一個try對應多個catch;
Servlet: Filter鏈式處理;
Spring MVC : 攔截器鏈(詳見: Spring MVC 實踐);
相關模式
Composite(組合)模式: 這種情況下, 一個構件的父構件可作為它的後繼者.