模式介紹
責任鏈模式是行為設計模式之一。
首先我們從字面去理解責任鏈模式:“責任”指的是一個人應盡的義務、分內應做的事;“鏈”指的是一個個小環首尾相連,組成的長鏈條。
為什么是“鏈”?因為“鏈”的每一環都是可以拆卸的,它們雖然是環環相扣,但是哪天我不想要中間的某一環,我只需要將其去下來即可,這大大提升了代碼的靈活性。
所以責任鏈模式通俗來講,指的是一件事情,有順序地傳遞給一群人,當第一個人無法處理時,再傳遞給第二個人去做,直到有人可以解決掉這件事情為止。
模式示例
小明是一家技術公司的測試,他除了完成本職工作外,還負責給技術部的小伙伴購買零食,不過零食錢是先由小明自己墊付的,接著到月底拿著發票去找領導報銷的流程。
這個月的月底,小明算了一下,一共花費了2450元。
接著小明拿著零食的發票去找組長報銷,結果組長說這金額太大了,只有500以下我才有權限簽字,你得去找技術總監。
接著小明找到技術總監,總監說2450太多啦,只有2000以下我才有權限簽字,你得去找咱們的老板。
接著小明去到老板的辦公室,老板看了一下說非常好,技術部的同學們都非常辛苦,以后都多買一些零食,接著麻溜地就簽了字,小明順利完成了此月的零食報銷請求。
使用場景
通過上述示例,我相信大家已經有些理解責任鏈模式了。
組長、總監、老板針對小明的報銷請求,都有自己要做的事情,但是根據小明的報銷金額不同,無法第一時間就知道到底是誰來處理這個報銷請求。
這就需要小明按照一定的順序,一個個地去問,直到找到能處理此次報銷請求的人為止。
這種情況下,就非常適合使用我們今天要說的責任鏈模式。
通過上面的描述,我們可以得出責任鏈模式的使用場景:
- 一個請求需要多個對象處理,但具體由哪個對象處理需要在運行時動態判斷時;
- 需要動態指定一組對象處理一個請求。
所含角色
責任鏈模式包含兩個主要角色:
- 處理者抽象(Handler):抽象的處理者,聲明一個處理請求的方法,并在其中保持對下一個處理者的引用。
- 處理者實現(HandlerImpl):處理者抽象的具體實現,對請求進行處理,如果無法處理,則通過下一個處理者的引用將其轉發下去。
具體代碼
針對上述示例和角色描述,我們來將其轉化成具體的代碼。
首先是處理者的抽象,針對上述示例,我們的抽象的請求處理方法應該為報銷:
public abstract class Leader {
//下一個處理者
private Leader nextLeader;
/**
* 處理報銷請求
*
* @param money 申請報銷的金額
*/
public final void handlerRequest(int money) {
if (money <= getSelfLimit()) {
//當申請的金額<=自己的處理額度時,將此次請求消化
handler(money);
} else {
//否則交給下一個處理者來處理
if (nextLeader != null) {
nextLeader.handlerRequest(money);
}
}
}
/**
* 獲取自身的額度,由具體實現來設置
*
* @return 自身能處理的額度
*/
public abstract int getSelfLimit();
/**
* 具體處理邏輯在這里實現
*
* @param money 申請報銷的金額
*/
public abstract void handler(int money);
public void setNextLeader(Leader nextLeader) {
this.nextLeader = nextLeader;
}
public Leader getNextLeader() {
return nextLeader;
}
}
代碼還是有些多的,讓我們來解釋一下這個處理者抽象類:
- nextLeader:指定了當請求無法處理時,下一級的處理者。這是責任鏈模式中,“鏈”的核心。對外暴露了set、get方法。
- handlerRequest(int money):注意此方法不是抽象并且是final修飾的,也就是無法重寫,無法修改。它其中的邏輯是用來判斷此次請求,應該是由自己消化,還是分發給下一個處理者。當你要開始請求時,只需要調用此方法即可。為了適應我們舉的示例,這里有一個money參數。這里說一下,每一種設計模式僅僅代表著一種編程思想,具體代碼還是要看需求的,如果需求復雜,那么這里分發的邏輯也會復雜、參數也會更多,反之亦然。
- getSelfLimit():抽象方法,指定處理者的最高處理額度。
- handler(int money):當此次請求該由自己消化時,具體的消化邏輯在這里實現。
如果還有疑問,不要急,我們先來具體看一個處理者的實現,可能你就會恍然大悟:
組長:
public class LeaderGroup extends Leader {
/**
* @return 組長報銷的處理額度
*/
@Override
public int getSelfLimit() {
return 500;
}
/**
* 當報銷金額 <= getSelfLimit() 時,請求會到這里來消化
*
* @param money 申請報銷的金額
*/
@Override
public void handler(int money) {
Toast.makeText(App.context, "小錢,組長報銷:" + money, Toast.LENGTH_SHORT).show();
}
}
組長繼承了抽象的處理者,并指定了組長的報銷額度以及具體的處理邏輯。
同樣的,總監和老板也是一樣的道理:
總監:
public class LeaderGeneral extends Leader {
/**
* @return 總監報銷的處理額度
*/
@Override
public int getSelfLimit() {
return 2000;
}
/**
* 當報銷金額 <= getSelfLimit() 時,請求會到這里來消化
*
* @param money 申請報銷的金額
*/
@Override
public void handler(int money) {
Toast.makeText(App.context, "金額挺大,總監報銷:" + money, Toast.LENGTH_SHORT).show();
}
}
老板:
public class LeaderCEO extends Leader {
/**
* @return 老板報銷的處理額度
*/
@Override
public int getSelfLimit() {
return Integer.MAX_VALUE;
}
/**
* 當報銷金額 <= getSelfLimit() 時,請求會到這里來消化
*
* @param money 申請報銷的金額
*/
@Override
public void handler(int money) {
Toast.makeText(App.context, "這么多錢,老板報銷:" + money, Toast.LENGTH_SHORT).show();
}
}
至此,我們的處理者抽象,以及處理者實現都已經完成了,下面我們來具體模擬一下報銷流程:
//獲取輸入框中輸入的金額
String money = et_money.getText().toString();
if (TextUtils.isEmpty(money)) {
Toast.makeText(this, "請輸入金額", Toast.LENGTH_SHORT).show();
return;
}
//組長實例
Leader group = new LeaderGroup();
//總監實例
Leader general = new LeaderGeneral();
//CEO實例
Leader CEO = new LeaderCEO();
//組長的下一級是總監
group.setNextLeader(general);
//總監的下一級是CEO
general.setNextLeader(CEO);
//由組長優先處理報銷
group.handlerRequest(Integer.valueOf(money));
上面的測試代碼邏輯也很清晰:
- 獲取到具體的報銷金額
- 首先創了組長、總監、CEO的實例
- 按照組長 -> 總監 -> 老板 的報銷順序將其進行排序
- 由組長優先處理報銷請求,當組長無法處理時,會由組長實例中的下一個處理者來處理。
當我們需求發生變化,比如組長的報銷金額變成了1000、比如現在組長沒有報銷權限了等等,你會發現非常地好維護。
這里我們假設總監無法報銷了,當組長無法報銷時,直接找到CEO來報銷:
Leader group = new LeaderGroup();
Leader CEO = new LeaderCEO();
group.setNextLeader(CEO);
group.handlerRequest(Integer.valueOf(money));
我們僅僅是將組長的下一個處理者改為老板即可,就可以完美刪除掉總監的存在。
這種“鏈”式的寫法,完美解耦了請求者和處理者,提高代碼了靈活性。
總結
到這里,一個最基本的責任鏈模式就完成了。
責任鏈模式的優點已經說過了。
缺點就是處理者太多的話,必定會影響性能,尤其是在一些遞歸調用中,使用時一定要注意。
感謝
《Android源碼設計模式解析與實戰》 何紅輝、關愛民 著