java與設計模式-責任鏈模式
一、 責任鏈模式的定義
責任鏈模式定義如下:
Avoid coupling the sender of a request to its receiver by giving more than one object a chance tohandle the request.Chain the receiving objects and pass the request along the chain until an objecthandles it.(使多個對象都有機會處理請求,從而避免了請求的發送者和接受者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有對象處理它爲止。)
責任鏈模式的重點是在“鏈”上,由一條鏈去處理相似的請求在鏈中決定誰來處理這個請求,並返回相應的結果,其如圖所示。
二、 責任鏈模式的類圖關係
三、 分析
-
抽象處理者
抽象的處理者實現三個職責:一是定義一個請求的處理方法handleMessage,唯一對外開放的方法;二是定義一個鏈的編排方法setNext,設置下一個處理者;三是定義了具體的請求 者必須實現的兩個方法:定義自己能夠處理的級別getHandlerLevel和具體的處理任務echo。
注意
在責任鏈模式中一個請求發送到鏈中後,前一節點消費部分消息,然後交由後續節點繼續處理,最終可以有處理結果也可以沒有處理結果,讀者可以不用理會什麼純的、不 純的責任鏈模式。同時,請讀者注意handlerMessage方法前的final關鍵字,可以閱讀的模板方法模式。
-
具體的處理者(一般是多個)
定義三個具體的處理者,以便可以組成一個鏈.
在處理者中涉及三個類:Level類負責定義請求和處理級別,Request類負責封裝請求,Response負責封裝鏈中返回的結果,該三個類都需要根據業務產生,讀者可以在實際應用中完成相關的業務填充,
最後,在場景類或高層模塊中對鏈進行組裝,並傳遞請求,返回結果.
四、示例Demo
- 抽象處理者
public abstract class Handler {
private Handler nextHandler;
/**
* 每個處理者都有一個處理級別
*
* @return
*/
protected abstract Level getHandlerLevel();
/**
* 每個處理者都必須實現處理任務
*
* @param request
* @return
*/
protected abstract Response echo(Request request);
/**
* 設置下一個處理者是誰
*
* @param nextHandler
*/
public void setNext(Handler nextHandler) {
this.nextHandler = nextHandler;
}
/**
* 每個處理者都必須對請求做出處理
*
* @param request
* @return
*/
public final Response handleMessage(Request request) {
Response response = null;
// 判斷是否是自己的處理級別
if (this.getHandlerLevel().equals(request.getLevel())) {
response = this.echo(request);
} else {
// 判斷是否有下一個處理者
if (null != this.nextHandler) {
response = this.nextHandler.handleMessage(request);
} else {
// 沒有適當的處理人,業務自行進行處理
}
}
return response;
}
}
-
具體的處理者
- 具體處理1
public class ConcreteHandler01 extends Handler {
/**
* 設置自己的處理級別
*
* @return
*/
@Override
protected Level getHandlerLevel() {
return null;
}
/**
* 定義自己的處理邏輯
*
* @param request
* @return
*/
@Override
protected Response echo(Request request) {
return null;
}
}
- 具體處理2
public class ConcreteHandler02 extends Handler {
@Override
protected Level getHandlerLevel() {
return null;
}
@Override
protected Response echo(Request request) {
return null;
}
}
- 具體處理3
public class ConcreteHandler03 extends Handler {
@Override
protected Level getHandlerLevel() {
return null;
}
@Override
protected Response echo(Request request) {
return null;
}
}
- 場景使用類
public class Main {
public static void main(String[] args) {
// 1. 聲明所有相關連
Handler handler01 = new ConcreteHandler01();
Handler handler02 = new ConcreteHandler02();
Handler handler03 = new ConcreteHandler03();
// 2. 設置連中的順序1->2->3
handler01.setNext(handler02);
handler02.setNext(handler03);
// 3. 提交結果,返回請求
handler01.handleMessage(new Request());
}
}
在實際應用中,一般會有一個封裝類對責任模式進行封裝,也就是替代Main類,直接返回鏈中的第一個處理者,具體鏈的設置不需要高層次模塊關係,這樣,更簡化了高層次模塊的調用,減少模塊間的耦合,提高系統的靈活性。
五、責任鏈模式的應用
-
責任鏈模式的優點
責任鏈模式非常顯著的優點是將請求和處理分開。請求者可以不用知道是誰處理的,處理者可以不用知道請求的全貌(例如在J2EE項目開發中,可以剝離出無狀態Bean由責任鏈處理),兩者解耦,提高系統的靈活性。
-
責任鏈模式的缺點
責任鏈有兩個非常顯著的缺點:一是性能問題,每個請求都是從鏈頭遍歷到鏈尾,特別是在鏈比較長的時候,性能是一個非常大的問題。二是調試不很方便,特別是鏈條比較長,環節比較多的時候,由於採用了類似遞歸的方式,調試的時候邏輯可能比較複雜。
-
責任鏈模式的注意事項
鏈中節點數量需要控制,避免出現超長鏈的情況,一般的做法是在Handler中設置一個最大節點數量,在setNext方法中判斷是否已經是超過其閾值,超過則不允許該鏈建立,避免無意識地破壞系統性能。
六、最佳實踐
在例子和通用源碼中Handler是抽象類,融合了模板方法模式,每個實現類只要實現兩個方法:echo方法處理請求和getHandlerLevel獲得處理級別,想想單一職責原則和迪米特法則吧,通過融合模板方法模式,各個實現類只要關注的自己業務邏輯就成了,至於說什麼事要自己處理,那就讓父類去決定好了,也就是說父類實現了請求傳遞的功能,子類實現請求的處理,符合單一職責原則,各個實現類只完成一個動作或邏輯,也就是隻有一個原因引起類的改變,我建議大家在使用的時候用這種方法,好處是非常明顯的了,子類的實現非常簡單,責任鏈的建立也是非常靈活的。
責任鏈模式屏蔽了請求的處理過程,你發起一個請求到底是誰處理的,這個你不用關心,只要你把請求拋給責任鏈的第一個處理者,最終會返回一個處理結果(當然也可以不做任何處理),作爲請求者可以不用知道到底是需要誰來處理的,這是責任鏈模式的核心,同時責任鏈模式也可以作爲一種補救模式來使用。舉個簡單例子,如項目開發的時候,需求確認是這樣的:一個請求(如銀行客戶存款的幣種),一個處理者(只處理人民幣),但是隨着業務的發展(改革開放了嘛,還要處理美元、日元等),處理者的數量和類型都有所增加,那這時候就可以在第一個處理者後面建立一個鏈,也就是責任鏈來處理請求,如果是人民幣,好,還是第一個業務邏輯來處理;如果是美元,好,傳遞到第二個業務邏輯來處理;日元、歐元……這些都不用在對原有的業務邏輯產生很大改變,通過擴展實現類就可以很好地解決這些需求變更的問題。
責任鏈在實際的項目中使用也是比較多的,曾經做過這樣一個項目,界面上有一個用戶註冊功能,註冊用戶分兩種,一種是VIP用戶,也就是在該單位辦理過業務的,一種是普通用戶,一個用戶的註冊要填寫一堆信息,VIP用戶只比普通用戶多了一個輸入項:VIP序列號。註冊後還需要激活,VIP和普通用戶的激活流程也是不同的,VIP是自動發送郵件到用戶的郵箱中就算激活了,普通用戶要發送短信才能激活,爲什麼呢?獲得手機號碼以後好發廣告短信啊!項目組就採用了責任鏈模式,甭管從前臺傳遞過來的是VIP用戶信息還是普通用戶信息,統一傳遞到一個處理入口,通過責任鏈來完成任務的處理。