一、責任鏈模式介紹
責任鏈模式(Iterator Pattern),是行爲型設計模式之一。生活中常見的鎖鏈,就是由一個個圓角長方形的鐵環串起來的結構。對於鏈式結構,每個節點都可以被拆開再連接,因此,鏈式結構也具有很好的靈活性。
將這樣一種結構應用於編程領域,將每一個節點看作是一個對象,每一個對象擁有不同的處理邏輯,將一個請求從鏈式的首端發出,沿着鏈的路徑依次傳遞給每一個節點對象,直至有對象處理這個請求爲止,我們將這樣的一種模式稱爲責任鏈模式。
二、責任鏈模式定義
使多個對象都有機會處理請求,從而避免了請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有對象處理它爲止。
責任鏈模式的使用場景
-
多個對象可以處理同一請求,但具體由哪個對象處理則在運行時動態決定。
-
在請求處理者不明確的情況下向多個對象中的一個提交一個請求。
-
需要動態指定一組對象處理請求。
三、責任鏈模式UML類圖
角色介紹
-
抽象處理者(Handler)角色:定義出一個處理請求的接口。如果需要,接口可以定義 出一個方法以設定和返回對下家的引用。這個角色通常由一個Java抽象類或者Java接口實現。上圖中Handler類的聚合關係給出了具體子類對下家的引用,抽象方法handleRequest()規範了子類處理請求的操作。
-
具體處理者(ConcreteHandler)角色:具體處理者接到請求後,可以選擇將請求處理掉,或者將請求傳給下家。由於具體處理者持有對下家的引用,因此,如果需要,具體處理者可以訪問下家。
四、責任鏈模式實現案例
這個例子我覺得很貼切。我們在公司有各種原因需要報銷費用,首先我們要找我們的上級領導去審批,報銷額度如果在領導的權限範圍內,那就審批通過,否則領導在找自己的上級去審批,以此類推。
1、抽象領導類
public abstract class Leader {
/**
* 上級領導處理者
*/
protected Leader nextHandler;
/**
* 處理報賬請求
* @param money 能批覆的報賬額度
*/
public final void handleRequest(int money){
System.out.println(getLeader());
if(money <=limit()){
handle(money);
}else{
System.out.println("報賬額度不足,提交領導");
if(null != nextHandler){
nextHandler.handleRequest(money);
}
}
}
/**
* 自身能批覆的額度權限
* @return 額度
*/
public abstract int limit();
/**
* 處理報賬行爲
* @param money 具體金額
*/
public abstract void handle(int money);
/**
* 獲取處理者
* @return 處理者
*/
public abstract String getLeader();
}
在這個抽象的領導類中只做了兩件事,一是定義了兩個抽象接口方法來確定一個領導者應有的行爲和屬性,二是聲明瞭一個處理報賬請求的方法來確定當前領導是否有能力處理報賬請求,如果沒有這個權限,則將該請求轉發給上級領導處理。
2、各個領導類的實現類
組長(額度1000):
public class GroupLeader extends Leader{
@Override
public int limit() {
return 1000;
}
@Override
public void handle(int money) {
System.out.println("組長批覆報銷"+ money +"元");
}
@Override
public String getLeader() {
return "當前是組長";
}
}
主管(額度5000):
public class Director extends Leader{
@Override
public int limit() {
return 5000;
}
@Override
public void handle(int money) {
System.out.println("主管批覆報銷"+ money +"元");
}
@Override
public String getLeader() {
return "當前是主管";
}
}
經理(額度10000):
public class Manager extends Leader{
@Override
public int limit() {
return 10000;
}
@Override
public void handle(int money) {
System.out.println("經理批覆報銷"+ money +"元");
}
@Override
public String getLeader() {
return "當前是經理";
}
}
老闆(額度…):
public class Boss extends Leader{
@Override
public int limit() {
return Integer.MAX_VALUE;
}
@Override
public void handle(int money) {
System.out.println("老闆批覆報銷"+ money +"元");
}
@Override
public String getLeader() {
return "當前是老闆";
}
}
3、發起申請
public class Client {
public static void main(String[] args) {
//構造各個領導對象
GroupLeader groupLeader = new GroupLeader();
Director director = new Director();
Manager manager = new Manager();
Boss boss = new Boss();
//設置上級領導處理者對象
groupLeader.nextHandler = director;
director.nextHandler = manager;
manager.nextHandler = boss;
//發起報賬申請
groupLeader.handleRequest(8000);
}
}
4、結果
當前是組長
報賬額度不足,提交領導
當前是主管
報賬額度不足,提交領導
當前是經理
經理批覆報銷8000元
責任鏈模式非常靈活,請求的發起可以從責任鏈的任何一個節點開始,也可以改變內部的傳遞規則。比如主管不在,我們完全可以跨過主管直接從組長那裏轉到經理。
對於責任鏈中的一個處理者對象,有兩個行爲。一是處理請求,二是將請求傳遞到下一節點,不允許某個處理者對象在處理了請求後又將請求傳送給上一個節點的情況。
對於一條責任鏈來說,一個請求最終只有兩種情況。一是被某個處理對象所處理,另一個是所有對象均未對其處理,對於前一種情況我們稱爲純的責任鏈模式,後一種爲不純的責任鏈。實際中大多爲不純的責任鏈。
五、Android源碼責任鏈模式
View事件的分發處理
ViewGroup事件投遞的遞歸調用就類似於一條責任鏈,一旦其尋找到責任者,那麼將由責任者持有並消費掉該次事件,具體體現在View的onTouchEvent方法中返回值的設置,如果返回false,那麼意味着當前的View不會是該次的責任人,將不會對其持有;如果返回true,此時View會持有該事件並不再向外傳遞。
Android view的樹形結構:
Activity–>>PhoneWindow–>>decorView–>>ViewGroup>View
首先按下一個頁面的按鈕後,事件第一個傳遞的對象就是Activity,然後往下執行傳遞,在事件傳遞過程中我們通過dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent三個方法來處理事件的請求。
-
dispatchTouchEvent (View和ViewGroup都有的方法)
事件下發
-
onInterceptTouchEvent (ViewGroup纔有的方法)
攔截下發的事件,並交給自己OnTouchEvent處理處理
-
onTouchEvent (View和ViewGroup都有的方法)
事件上報
六、總結
優點
- 降低耦合度:客戶端不需要知道請求由哪個處理者處理,而處理者也不需要知道處理者之間的傳遞關係,由系統靈活的組織和分配。
- 良好的擴展性:增加處理者的實現很簡單,只需重寫處理請求業務邏輯的方法。
缺點
- 請求會從鏈頭髮出,直到有處理者響應,在責任鏈比較長的時候會影響系統性能。
- 請求遞歸,調試排錯比較麻煩。
對於責任鏈中的一個處理者對象,其只有兩個行爲,一是處理請求,二是將請求轉送給下一個節點,不允許某個處理者對象在處理了請求後又將請求轉送給上一級節點的情況。
對於一條責任鏈來說,一個請求最終只有兩種情況,一是被某個處理對象所處理,另一個是所有對象均未對其處理,對於前一種情況我們稱該責任鏈爲純的責任鏈,對於後一種情況我們稱爲不純的責任鏈,在實際應用中,我們所見到的責任鏈模式大多爲不純的責任鏈。