責(zé)任鏈模式的介紹
? 責(zé)任鏈模式(Iterator Pattren),是行為型設(shè)計(jì)模式之一。什么是“鏈”雌贱?我們將多個(gè)節(jié)點(diǎn)首尾相連所構(gòu)成的模型稱為鏈蜓萄,比如生活中常見(jiàn)的鎖鏈,就是由一個(gè)個(gè)圓角長(zhǎng)方形的鐵環(huán)串起來(lái)的結(jié)構(gòu)鞠眉。對(duì)于鏈?zhǔn)浇Y(jié)構(gòu)薯鼠,每個(gè)節(jié)點(diǎn)都可以被拆開(kāi)再連接择诈,因此,鏈?zhǔn)浇Y(jié)構(gòu)也具有很好的靈活性出皇。將這樣一種結(jié)構(gòu)應(yīng)用于編程領(lǐng)域羞芍,將每個(gè)節(jié)點(diǎn)看作是一個(gè)對(duì)象,每個(gè)對(duì)象擁有不同的處理邏輯郊艘,將一個(gè)請(qǐng)求從鏈?zhǔn)降氖锥税l(fā)出荷科,沿著鏈的路徑一次傳遞給每一個(gè)節(jié)點(diǎn)對(duì)象,直至有對(duì)象處理這個(gè)請(qǐng)求為止纱注,我們將這樣的一種模式稱為責(zé)任鏈模式畏浆。
責(zé)任鏈模式的定義
? 使多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求,從而避免了請(qǐng)求的發(fā)送者和接收者之間的耦合關(guān)系奈附。將這些對(duì)象連成一條鏈全度,并沿著這條鏈傳遞該請(qǐng)求,直到有對(duì)象處理它為止斥滤。
責(zé)任鏈模式的使用場(chǎng)景
- 多個(gè)對(duì)象可以處理同一請(qǐng)求将鸵,但具體由哪個(gè)對(duì)象處理則在運(yùn)行時(shí)動(dòng)態(tài)決定。
- 在請(qǐng)求處理者不明確的情況下向多個(gè)對(duì)象中的一個(gè)提交一個(gè)請(qǐng)求佑颇。
- 需要?jiǎng)討B(tài)指定一組對(duì)象處理請(qǐng)求顶掉。
責(zé)任鏈模式的UML類圖
根據(jù)類圖我們可以得出如下一個(gè)責(zé)任鏈模式簡(jiǎn)化版通用模式代碼。
//抽象處理類
public abstract class Handler {
protected Handler successor; //下一個(gè)節(jié)點(diǎn)的處理者
/**
* 請(qǐng)求處理
* @param conditon 請(qǐng)求條件
*/
public abstract void handlerRequest(String conditon);
}
//具體的處理者1
public class ConcreteHandler1 extends Handler {
@Override
public void handlerRequest(String conditon) {
if (conditon.equals("ConcreteHandler1")) {
System.out.println("ConcreteHandler1 handler");
} else {
successor.handlerRequest(conditon);
}
}
}
//具體處理者2
public class ConcreteHandler2 extends Handler {
@Override
public void handlerRequest(String conditon) {
if (conditon.equals("ConcreteHandler2")) {
System.out.println("ConcreteHandler2 handler");
} else {
successor.handlerRequest(conditon);
}
}
}
//客戶類
public class Client {
@Test
public void test() {
//構(gòu)造一個(gè)ConcreteHandler1對(duì)象
ConcreteHandler1 handler1 = new ConcreteHandler1();
//構(gòu)造一個(gè)ConcreteHandler2對(duì)象
ConcreteHandler2 handler2 = new ConcreteHandler2();
//設(shè)置handler1的下一個(gè)節(jié)點(diǎn)
handler1.successor = handler2;
//設(shè)置handler2的下一個(gè)節(jié)點(diǎn)
handler2.successor = handler1;
//處理請(qǐng)求
handler1.handlerRequest("ConcreteHandler2");
}
}
角色介紹
-
Handler :抽象處理者角色挑胸,聲明一個(gè)請(qǐng)求處理的方法痒筒,并在其中保持一個(gè)對(duì)下一個(gè)處理節(jié)
Handler
對(duì)象的引用。 - ConcreteHandler :具體的處理者對(duì)請(qǐng)求進(jìn)行處理 茬贵,如果不能處理則將該請(qǐng)求轉(zhuǎn)發(fā)給下一個(gè)節(jié)點(diǎn)上的處理對(duì)象簿透。
上面我們說(shuō)這個(gè)一個(gè)簡(jiǎn)化版的通用模板代碼,為什么這個(gè)說(shuō)呢解藻?因?yàn)閷?duì)于請(qǐng)求來(lái)說(shuō)老充,其形式是固定的,就是一個(gè)字符串螟左,而判斷一個(gè)節(jié)點(diǎn)上的對(duì)象是否能夠處理該請(qǐng)求的標(biāo)志啡浊,則是該字符串是否與之匹配。然而 在大多數(shù)情況下胶背,責(zé)任鏈中的請(qǐng)求和對(duì)應(yīng)的處理規(guī)則是不盡相同的巷嚣,在這種情況下可以將請(qǐng)求進(jìn)行封裝,同時(shí)對(duì)請(qǐng)求的處理規(guī)則也進(jìn)行封裝作為一個(gè)獨(dú)立的對(duì)象钳吟,類圖如下所示:
首先我們看下AbstractHandler
抽象處理者廷粒,其聲明了處理者對(duì)象處理請(qǐng)求的方法和獲取處理級(jí)別的方法,并對(duì)具體的處理轉(zhuǎn)發(fā)邏輯進(jìn)行了實(shí)現(xiàn)砸抛。
public abstract class AbstractHandler {
protected AbstractHandler nextHandler;
public final void handleRequest(AbstractRequest request){
//判斷當(dāng)前處理者對(duì)象的處理級(jí)別是否與請(qǐng)求者的處理級(jí)別一致
if (getHandleLevel()==request.getHandleLevel()){
handle(request);
}else {
//否則將請(qǐng)求對(duì)象轉(zhuǎn)發(fā)給下一個(gè)節(jié)點(diǎn)上的請(qǐng)求對(duì)象
if (nextHandler!=null){
nextHandler.handleRequest(request);
}else {
//當(dāng)所有處理者對(duì)象均不能處理該請(qǐng)求輸出
System.out.println("All of haandler can't handle the request");
}
}
}
/**
* 獲取處理者對(duì)象的處理級(jí)別
* @return 處理級(jí)別
*/
protected abstract int getHandleLevel();
/**
* 每個(gè)處理者對(duì)象的具體處理方式
* @param request 請(qǐng)求者對(duì)象
*/
protected abstract void handle(AbstractRequest request);
}
在這種情況下我們的責(zé)任轉(zhuǎn)發(fā)邏輯由抽象處理類控制评雌,而對(duì)于抽象請(qǐng)求者树枫,其內(nèi)部也聲明了一個(gè)獲取請(qǐng)求級(jí)別的方法,其與抽象處理者中返回的處理級(jí)別保持對(duì)應(yīng)景东,什么級(jí)別的處理邏輯就對(duì)應(yīng)什么樣的請(qǐng)求級(jí)別砂轻。
//抽象請(qǐng)求類
public abstract class AbstractRequest {
//處理 對(duì)象
private Object obj;
public AbstractRequest(Object obj) {
this.obj = obj;
}
/**
* 獲取處理的內(nèi)容對(duì)象
* @return 具體的處理對(duì)象
*/
public Object getContent(){
return obj;
}
/**
* 獲取請(qǐng)求級(jí)別
* @return 請(qǐng)求級(jí)別
*/
public abstract int getHandleLevel();
}
下面我們分別實(shí)現(xiàn)3個(gè)請(qǐng)求者和3 個(gè)處理者對(duì)象 ,邏輯很簡(jiǎn)單斤吐。
public class Request1 extends AbstractRequest {
public Request1(Object obj) {
super(obj);
}
@Override
public int getHandleLevel() {
return 1;
}
}
public class Request2 extends AbstractRequest {
public Request2(Object obj) {
super(obj);
}
@Override
public int getHandleLevel() {
return 2;
}
}
public class Request3 extends AbstractRequest {
public Request3(Object obj) {
super(obj);
}
@Override
public int getHandleLevel() {
return 3;
}
}
處理者
public class Handler1 extends AbstractHandler {
@Override
protected int getHandleLevel() {
return 1;
}
@Override
protected void handle(AbstractRequest request) {
System.out.println("Handle1 handle request:"+request.getHandleLevel());
}
}
public class Handler2 extends AbstractHandler {
@Override
protected int getHandleLevel() {
return 2;
}
@Override
protected void handle(AbstractRequest request) {
System.out.println("Handle2 handle request:"+request.getHandleLevel());
}
}
public class Handler3 extends AbstractHandler {
@Override
protected int getHandleLevel() {
return 2;
}
@Override
protected void handle(AbstractRequest request) {
System.out.println("Handle3 handle request:"+request.getHandleLevel());
}
}
下面是客戶類
public class Client {
@Test
public void main(){
//構(gòu)造三個(gè)處理者對(duì)象
Handler1 handler1 = new Handler1();
Handler2 handler2 = new Handler2();
Handler3 handler3 = new Handler3();
//設(shè)置當(dāng)前處理者對(duì)象的下一個(gè)節(jié)點(diǎn)
handler1.nextHandler = handler2;
handler2.nextHandler = handler3;
//構(gòu)造三個(gè)請(qǐng)求者對(duì)象
Request1 request1 = new Request1("Request1");
Request2 request2 = new Request2("Request2");
Request3 request3 = new Request3("Request3");
//總是從鏈?zhǔn)降氖锥税l(fā)起請(qǐng)求
handler1.handleRequest(request1);
handler2.handleRequest(request2);
handler1.handleRequest(request3);
}
}
結(jié)果:
Handle1 handle request:1
Handle2 handle request:2
Handle3 handle request:3
責(zé)任鏈模式的簡(jiǎn)單實(shí)現(xiàn)
舉個(gè)例子:公司安排小明出差搔涝,出差回來(lái)后花費(fèi)了近5萬(wàn)元,于是小明第二天上班找組長(zhǎng)申請(qǐng)報(bào)銷費(fèi)用和措,組長(zhǎng)一看是一筆不小的數(shù)目庄呈,他沒(méi)有權(quán)限審批,于是組長(zhǎng)拿著票據(jù)去找部門主管派阱,主管一看要報(bào)這么多錢诬留,自己權(quán)限只能批5千以下的費(fèi)用,這完全超出了自己的范圍贫母,于是主管又去找經(jīng)理文兑,經(jīng)理一看二話不說(shuō)直接拿著票據(jù)去了老板的辦公室,因?yàn)樗荒芘蝗f(wàn)以下的費(fèi)用腺劣,上面的情景就是一個(gè)責(zé)任鏈模式的小例子绿贞,每一個(gè)人,準(zhǔn)確的說(shuō)是每一類人代表這條鏈上的一個(gè)節(jié)點(diǎn)橘原,小明是請(qǐng)求的發(fā)起者籍铁,而老板則是出于鏈條頂端的類,小明在鏈條底端發(fā)起一個(gè)申請(qǐng)報(bào)賬的請(qǐng)求趾断,首先由組長(zhǎng)處理該請(qǐng)求拒名,組長(zhǎng)對(duì)比后發(fā)現(xiàn)自己權(quán)限不夠于是將該請(qǐng)求轉(zhuǎn)發(fā)給位于鏈中的下一個(gè)節(jié)點(diǎn)的主管,主管對(duì)比后也發(fā)現(xiàn)自己的權(quán)限不夠又將該請(qǐng)求轉(zhuǎn)發(fā)給經(jīng)理芋酌,而經(jīng)理也基于同樣的原因?qū)⒄?qǐng)求轉(zhuǎn)發(fā)給老板靡狞,這樣層層轉(zhuǎn)達(dá)直至請(qǐng)求被處理,從中大家可以看到一個(gè)顯而易見(jiàn)的事隔嫡,就是至始至終小明只與組長(zhǎng)產(chǎn)生的關(guān)聯(lián),后面具體由誰(shuí)處理的票據(jù)甘穿,小明并不關(guān)心腮恩,唯一在乎的是報(bào)賬的結(jié)果,責(zé)任鏈模式在這里很好地將請(qǐng)求的發(fā)起者與處理者解耦温兼。我們?cè)诖a 中模擬這個(gè)過(guò)程秸滴,首先還是先聲明一個(gè)抽象的領(lǐng)導(dǎo)類。
//抽象領(lǐng)導(dǎo)者
public abstract class Leader {
//上一級(jí)領(lǐng)導(dǎo)處理者
protected Leader nexthandler;
public void handlerRequest(int money){
if (money<=limit()) {
handle(money);
}else {
if (nexthandler != null) {
nexthandler.handle(money);
}
}
}
/**
* 自身能審批的額度
* @return 額度
*/
public abstract int limit();
/**
* 處理報(bào)賬行為
* @param money 具體金額
*/
public abstract void handle(int money);
}
在這個(gè)抽象的領(lǐng)導(dǎo)類中只做了兩件事募判,一是定義了兩個(gè)抽象接口方法來(lái)確定一個(gè)領(lǐng)導(dǎo)者應(yīng)有的行為和屬性荡含,而是聲明了一個(gè)處理報(bào)賬請(qǐng)求的方法來(lái)確定當(dāng)前領(lǐng)導(dǎo)是否有能力處理報(bào)賬請(qǐng)求咒唆,如果沒(méi)有這個(gè)權(quán)限,則將該請(qǐng)求轉(zhuǎn)發(fā)給上一級(jí)的領(lǐng)導(dǎo)處理释液,接下來(lái)是各個(gè)領(lǐng)導(dǎo)類的實(shí)現(xiàn)全释。
public class GroupLeader extends Leader {
@Override
public int limit() {
return 1000;
}
@Override
public void handle(int money) {
System.out.println("組長(zhǎng)審批報(bào)銷"+money +"元");
}
}
public class Director extends Leader {
@Override
public int limit() {
return 5000;
}
@Override
public void handle(int money) {
System.out.println("主管審批報(bào)銷"+money +"元");
}
}
public class Manager extends Leader {
@Override
public int limit() {
return 10000;
}
@Override
public void handle(int money) {
System.out.println("經(jīng)理審批報(bào)銷"+money +"元");
}
}
public class Boss extends Leader {
@Override
public int limit() {
return Integer.MAX_VALUE;
}
@Override
public void handle(int money) {
System.out.println("老板審批報(bào)銷"+money +"元");
}
}
最后小明從組長(zhǎng)開(kāi)始請(qǐng)求申請(qǐng)報(bào)帳。
@Test
public void main() {
//構(gòu)造各個(gè)領(lǐng)導(dǎo)
GroupLeader groupLeader = new GroupLeader();
Director director = new Director();
Manager manager = new Manager();
Boss boss = new Boss();
//設(shè)置上一級(jí)領(lǐng)導(dǎo)處理者
groupLeader.nexthandler = director;
director.nexthandler = manager;
manager.nexthandler = boss;
//發(fā)起請(qǐng)求
groupLeader.handlerRequest(50000);
}
結(jié)果
老板審批報(bào)銷50000元
這里大家可能會(huì)想误债,可不可以直接越過(guò)組長(zhǎng)找組長(zhǎng)報(bào)賬呢浸船?答案是肯定的,這也是責(zé)任鏈模式的靈活之處寝蹈,請(qǐng)求的發(fā)起可以從責(zé)任鏈的任何一個(gè)節(jié)點(diǎn)開(kāi)始李命,同時(shí)也可以改變責(zé)任鏈內(nèi)部的傳遞規(guī)則,如果主管不在箫老,我們完全可以跨過(guò)主管從組長(zhǎng)直接將請(qǐng)求轉(zhuǎn)送給經(jīng)理封字。
對(duì)于責(zé)任鏈中的一個(gè)處理者對(duì)象,其中只有兩個(gè)行為耍鬓,一是請(qǐng)求處理阔籽,而是將請(qǐng)求轉(zhuǎn)送給下一個(gè)節(jié)點(diǎn),不允許某個(gè)處理者對(duì)象在處理了請(qǐng)求后又將請(qǐng)求轉(zhuǎn)發(fā)給上一個(gè)節(jié)點(diǎn)的情況界斜。對(duì)于一條責(zé)任鏈來(lái)說(shuō)仿耽,一個(gè)請(qǐng)求最終只有兩種情況,一是被某個(gè)處理對(duì)象所處理各薇,另一個(gè)是所有對(duì)象均未對(duì)其處理项贺,對(duì)于前一個(gè)情況我們稱該責(zé)任鏈為純的責(zé)任鏈,對(duì)于后一種情況我們稱為不純的責(zé)任鏈峭判,在實(shí)際應(yīng)用中开缎,我們所見(jiàn)到的責(zé)任鏈模式大多為不純的責(zé)任鏈。
責(zé)任鏈模式的實(shí)戰(zhàn)
責(zé)任鏈模式與其說(shuō)實(shí)在Android
中的應(yīng)用倒不如說(shuō)是在Java
中的應(yīng)用林螃,畢竟上層應(yīng)用開(kāi)發(fā)大多都是基于Java
的奕删,而我們的設(shè)計(jì)模式也是針對(duì)Java
而言,Android
中我們可以借鑒責(zé)任鏈模式的思想來(lái)優(yōu)化BroadcastReceiver
使之成為一個(gè)全局的責(zé)任處理者疗认,具體方法很簡(jiǎn)單完残,我們知道Broadcast
可以分為兩種,一種是Normal Broadcast
普通廣播横漏,另一種是Order Broadcast
有序廣播谨设,普通廣播是異步的,發(fā)出時(shí)可以被所有的接收者收到缎浇;而有序廣播則是根據(jù)優(yōu)先級(jí)一次傳播的扎拣,直到有接收者將其終止或所有接收者都不終止它,有序廣播的這一特性與我們的責(zé)任鏈模式很想近,通過(guò)它可以實(shí)現(xiàn)一種全局的責(zé)任鏈?zhǔn)录幚矶叮@里我們創(chuàng)建3個(gè) BroadcastReceiver
誉券。
public class FirstReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//獲取Intent中附加的限制值
int limit = intent.getIntExtra("limit", -1001);
//如果限制值等于1000則處理,否則繼續(xù)轉(zhuǎn)發(fā)給下一個(gè)Receiver
if (limit!=1000) {
Bundle bundle = new Bundle();
bundle.putString("new","Message from FirstReceiver");
setResultExtras(bundle);
}else {
String msg = intent.getStringExtra("msg");
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
}
}
在發(fā)送廣播的時(shí)候我們?cè)?code>Intent 中附加兩個(gè)值刊愚,一個(gè)int類型的值用于存儲(chǔ)權(quán)限值踊跟,另一個(gè)String類型的則存儲(chǔ)消息,一個(gè)接收者能否處理本次廣播的唯一條件則是看廣播中所附加的權(quán)限值是否與自身相等如上面FirstReceiver
的處理權(quán)限值是1000百拓;則只有當(dāng)limit
=1000時(shí)才會(huì)處理本次廣播琴锭,否則在廣播的Intent
中在附加一條信息傳遞出去給下一個(gè)接收者。
public class FirstReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//獲取Intent中附加的限制值
int limit = intent.getIntExtra("limit", -1001);
//如果限制值等于1000則處理衙传,否則繼續(xù)轉(zhuǎn)發(fā)給下一個(gè)Receiver
if (limit!=1000) {
Bundle bundle = new Bundle();
bundle.putString("new","Message from FirstReceiver");
setResultExtras(bundle);
}else {
String msg = intent.getStringExtra("msg");
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
}
}
public class SecondReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//獲取Intent中附加的限制值
int limit = intent.getIntExtra("limit", -1001);
//如果限制值等于1000則處理决帖,否則繼續(xù)轉(zhuǎn)發(fā)給下一個(gè)Receiver
if (limit!=100) {
Bundle bundle = new Bundle();
bundle.putString("new","Message from SecondReceiver");
setResultExtras(bundle);
}else {
String msg = intent.getStringExtra("msg");
Bundle bundle = getResultExtras(true);
String str = bundle.getString("new");
Toast.makeText(context,msg+"<<<>>>" +str, Toast.LENGTH_SHORT).show();
abortBroadcast();
}
}
}
public class ThridReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//獲取Intent中附加的限制值
int limit = intent.getIntExtra("limit", -1001);
//如果限制值等于1000則處理,否則繼續(xù)轉(zhuǎn)發(fā)給下一個(gè)Receiver
if (limit!=10) {
Bundle bundle = new Bundle();
bundle.putString("new","Message from ThridReceiver");
setResultExtras(bundle);
}else {
String msg = intent.getStringExtra("msg");
Bundle bundle = getResultExtras(true);
String str = bundle.getString("new");
Toast.makeText(context,msg+"<<<>>>" +str, Toast.LENGTH_SHORT).show();
abortBroadcast();
}
}
}
最后我們還要在清單文件聲明3個(gè)Receiver,并設(shè)定對(duì)應(yīng)的權(quán)限值蓖捶。
<receiver android:name=".FirstReceiver" android:exported="false">
<intent-filter android:priority="1000">
<action android:name="ORDER_BROADCAST" />
</intent-filter>
</receiver>
<receiver android:name=".SecondReceiver" android:exported="false">
<intent-filter android:priority="100">
<action android:name="ORDER_BROADCAST" />
</intent-filter>
</receiver>
<receiver android:name=".ThridReceiver" android:exported="false">
<intent-filter android:priority="10">
<action android:name="ORDER_BROADCAST" />
</intent-filter>
</receiver>
完成后地回,我們?cè)谝粋€(gè)Activity
中發(fā)送一個(gè)廣播
Intent intent = new Intent();
intent.setAction("ORDER_BROADCAST");
intent.putExtra("limit",1000);
intent.putExtra("msg","Message from MainActivity");
sendOrderedBroadcast(intent,null);
這里將limit
設(shè)置為100
,也就是說(shuō)只有SecondReceiver
才會(huì)處理它俊鱼。
總結(jié)
- 優(yōu)點(diǎn):可以對(duì)請(qǐng)求者和處理者關(guān)系解耦刻像,提高代碼的靈活性。
- 缺點(diǎn):對(duì)鏈中請(qǐng)求處理者的遍歷并闲,如果處理者太多细睡,那么遍歷必定會(huì)影響性能,特別是在一些遞歸調(diào)用中帝火。
Demo
參考
《Android源碼設(shè)計(jì)模式》