概念
責任鏈模式爲某個請求創建一個對象鏈,每個對象依次檢查此請求,並對其進行處理,或者將它傳給鏈中的下一個對象。
鏈表是很常見一種數據結構,責任鏈模式的結構其實和鏈表相差無幾,唯一的區別就是責任鏈模式中所有的對象都有一個共同的父類(或接口):
在責任鏈模式中,N個 Handler 子類都處理同一個請求,只不過具體的職責有所差別。
當有一個請求進入時,先經過 AHandler 的 handlerRequest 方法,然後再把請求傳遞給 BHandler,B 處理完再把請求傳遞給 CHandler,以此類推,形成一個鏈條。鏈條上的每一個對象所承擔的責任各不相同,這就是責任鏈模式。
例子
現在我們模擬一個場景:論壇用戶發表帖子,但是常常會有用戶一些不良的信息,如廣告信息,涉黃信息,涉及政治的敏感詞等。這時我們就可以使用責任鏈模式來過濾用戶發表的信息。
先定義所有責任鏈對象的父類:
/**
* 帖子處理器
*/
public abstract class PostHandler {
/**
* 後繼者
*/
protected PostHandler successor;
public void setSuccessor(PostHandler handler){
this.successor = handler;
}
public abstract void handlerRequest(Post post);
protected final void next(Post post){
if(this.successor != null){
this.successor.handlerRequest(post);
}
}
}
父類 Handler 主要封裝了傳遞請求等方法,其中要注意的有兩點:
- successor,後繼者,這個屬性很重要,它保存了責任鏈中下一個處理器
- 在 next() 方法中(方法名自己隨便取),當請求傳遞到最後一個責任對象時,已經沒有後繼者繼續處理請求了,因此要對 successor 做判空處理,避免拋出空指針異常。
- 處理請求的handlerRequest 的入參和返回類型可以根據實際情況修改,可以在該方法中拋出異常來中斷請求
廣告處理器:
/**
* 廣告處理器
*/
public class AdHandler extends PostHandler {
@Override
public void handlerRequest(Post post) {
//屏蔽廣告內容
String content = post.getContent();
//.....
content = content.replace("廣告","**");
post.setContent(content);
System.out.println("過濾廣告...");
//傳遞給下一個處理器
next(post);
}
}
涉黃處理器:
/**
* 涉黃處理器
*/
public class YellowHandler extends PostHandler {
@Override
public void handlerRequest(Post post) {
//屏蔽涉黃內容
String content = post.getContent();
//.....
content = content.replace("涉黃","**");
post.setContent(content);
System.out.println("過濾涉黃內容...");
//傳遞給下一個處理器
next(post);
}
}
調用:
//創建責任對象
PostHandler adHandler = new AdHandler();
PostHandler yellowHandler = new YellowHandler();
PostHandler swHandler = new SensitiveWordsHandler();
//形成責任鏈
yellowHandler.setSuccessor(swHandler);
adHandler.setSuccessor(yellowHandler);
Post post = new Post();
post.setContent("我是正常內容,我是廣告,我是涉黃,我是敏感詞,我是正常內容");
System.out.println("過濾前的內容爲:"+post.getContent());
post = adHandler.handlerRequest(post);
System.out.println("過濾後的內容爲:"+post.getContent());
總結
可能大家有疑問,爲什麼不把所有的過濾器寫在一個方法裏呢?因爲這樣做又破壞了開放封閉原則。我們需要使用責任鏈模式,能夠在不修改已有代碼的情況下擴展新功能。
責任鏈模式將常用於過濾器,攔截器,事件(鼠標鍵盤事件,冒泡事件等)等場景
優點
- 請求者和接收者解耦
可以動態的增加或減少責任鏈上的對象,或者修改順序
缺點
- 調用者不知道請求可能被哪些責任鏈對象處理,不利於排錯
用戶請求可能被責任鏈中途攔截,最終未必被真正執行,這點既是優點也是缺點,我們可以利用它做權限控制攔截器