項(xiàng)目做完了,作為老大的你決定叫上項(xiàng)目組的人一起聚個(gè)餐犒勞下大家,于是你去財(cái)務(wù)申請(qǐng)聚餐費(fèi)用,財(cái)務(wù)告訴你聚餐費(fèi)用申請(qǐng)的流程如下:
- 小于500元,項(xiàng)目經(jīng)理可以審批
- 大于500小于1000,需要部門經(jīng)理審批
- 大于1000的都需要總經(jīng)理審批
如果讓你來實(shí)現(xiàn)這個(gè)流程,該如何做呢?常規(guī)做法很簡單,如下所示:
#import "feeRequest.h"
@implementation feeRequest
-(void)applayForFee:(NSInteger)fee{
if (fee < 500){
[self projectHandle];
}else if(fee < 1000 && fee > 500){
[self depManagerHandle];
}else{
[self generalManagerHandle];
}
}
-(void)projectHandle{
NSLog(@"項(xiàng)目經(jīng)理同意了費(fèi)用申請(qǐng)");
}
-(void)depManagerHandle{
NSLog(@"部門經(jīng)理同意了費(fèi)用申請(qǐng)");
}
-(void)generalManagerHandle{
NSLog(@"總經(jīng)理同意了費(fèi)用申請(qǐng)");
}
@end
實(shí)現(xiàn)起來很簡單吧。但是仔細(xì)分析下,上面的寫法有如下兩個(gè)問題
申請(qǐng)費(fèi)用的流程可能會(huì)經(jīng)常變動(dòng)。現(xiàn)在是項(xiàng)目經(jīng)理-->部門經(jīng)理--->總經(jīng)理,如果哪天流程倒過來呢?或者加入了新的審批的人呢?上面的代碼就無可避免的需要更改
各個(gè)審批流程可能會(huì)更改。現(xiàn)在的三個(gè)審批流程如上所示,但是可能每個(gè)經(jīng)理的審批的金額會(huì)有所更改,這個(gè)時(shí)候也需要更改上面的代碼
抽象下上面的流程:客戶提出一個(gè)請(qǐng)求(申請(qǐng)聚餐費(fèi)用),會(huì)有很多對(duì)象(經(jīng)理)來處理這個(gè)請(qǐng)求,每個(gè)對(duì)象的處理邏輯是不同的,如果對(duì)象自己可以處理這個(gè)請(qǐng)求,就會(huì)處理完成然后把結(jié)果返回給客戶,如果自己不能處理,就交給其他對(duì)象處理。而且希望上面的流程可以靈活變動(dòng),處理請(qǐng)求的對(duì)象可以隨意組合替換,來適應(yīng)新的業(yè)務(wù)需求。
要實(shí)現(xiàn)上述功能,可以使用職責(zé)鏈模式,下面就來具體看看
定義
使多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求,從而避免請(qǐng)求的發(fā)送者和接收者之間的耦合關(guān)系。將這 些對(duì)象連成一條鏈,并沿著這條鏈傳遞該請(qǐng)求,直到有一個(gè)對(duì)象處理它為止。
分析下上面的業(yè)務(wù)場景,客戶端提取申請(qǐng)費(fèi)用的請(qǐng)求,然后項(xiàng)目經(jīng)理、部門經(jīng)理、總經(jīng)理依次處理這個(gè)請(qǐng)求,他們組成了一個(gè)請(qǐng)求處理鏈(項(xiàng)目經(jīng)理-->部門經(jīng)理--->總經(jīng)理),客戶請(qǐng)求在這個(gè)鏈中傳遞,直到一個(gè)對(duì)象處理了這個(gè)請(qǐng)求才結(jié)束,否則一直向下傳遞到鏈的末尾,這正是職責(zé)鏈的功能。
要想讓流程的處理靈活多變,可以隨意組合和替換,就需要?jiǎng)討B(tài)構(gòu)建流程處理的步驟,每個(gè)步驟實(shí)現(xiàn)一個(gè)功能,然后把這些步驟串聯(lián)起來實(shí)現(xiàn)完整的請(qǐng)求處理鏈。
我們要實(shí)現(xiàn)發(fā)送者和接受者解耦,那么就需要讓提交幫助請(qǐng)求的對(duì)象不需要知道誰是最終提供幫助的對(duì)象。職責(zé)鏈模式給多個(gè)對(duì)象處理一個(gè)請(qǐng)求的機(jī)會(huì),從而解耦發(fā)送者和接受者。該請(qǐng) 求沿對(duì)象鏈傳遞直至其中一個(gè)對(duì)象處理它,從第一個(gè)對(duì)象開始,鏈中收到請(qǐng)求的對(duì)象要么親自處理它,要么轉(zhuǎn)發(fā)給鏈中的下一個(gè)候 選者。提交請(qǐng)求的對(duì)象并不明確地知道哪一個(gè)對(duì)象將會(huì)處理它,但是最終會(huì)有一個(gè)隱式對(duì)象來保證請(qǐng)求一定會(huì)被處理。
由上述分析可知,職責(zé)鏈模式適用于如下幾種情況:
- 有多個(gè)的對(duì)象可以處理一個(gè)請(qǐng)求,但是具體是哪個(gè)對(duì)象處理該請(qǐng)求則是在運(yùn)行時(shí)刻才確定。
- 想在不明確指定接收者的情況下,向多個(gè)對(duì)象中的一個(gè)提交請(qǐng)求。
- 一個(gè)請(qǐng)求的處理鏈可以動(dòng)態(tài)設(shè)置
UML結(jié)構(gòu)圖及說明
代碼實(shí)現(xiàn)
1、定義抽象類handler
#import <Foundation/Foundation.h>
@interface handler : NSObject
@property(strong,nonatomic)handler *successor;
-(void)handleRequest:(NSInteger)fee;
@end
==================
#import "handler.h"
@implementation handler
-(void)handleRequest:(NSInteger)fee{
}
@end
2、實(shí)現(xiàn)三個(gè)具體的職責(zé)鏈成員
#import "handler.h"
@interface projectManagerHandler : handler
@end
=============================
#import "projectManagerHandler.h"
@implementation projectManagerHandler
-(void)handleRequest:(NSInteger)fee{
if (fee < 500) {
NSLog(@"項(xiàng)目經(jīng)理同意了費(fèi)用申請(qǐng)");
}else{
if (self.successor)
NSLog(@"項(xiàng)目經(jīng)理沒有權(quán)限批準(zhǔn),轉(zhuǎn)到部門經(jīng)理處理");
[self.successor handleRequest:fee];
}
}
@end
#import "handler.h"
@interface depManagerHandler : handler
@end
=====================
#import "depManagerHandler.h"
@implementation depManagerHandler
-(void)handleRequest:(NSInteger)fee{
if (fee > 500 && fee < 1000) {
NSLog(@"部門經(jīng)理同意了費(fèi)用申請(qǐng)");
}else{
if (self.successor)
NSLog(@"部門經(jīng)理沒有權(quán)限批準(zhǔn),轉(zhuǎn)到總經(jīng)理處理");
[self.successor handleRequest:fee];
}
}
@end
#import "handler.h"
@interface generalManagerHandler : handler
@end
=====================
#import "generalManagerHandler.h"
@implementation generalManagerHandler
-(void)handleRequest:(NSInteger)fee{
NSLog(@"總經(jīng)理同意了費(fèi)用申請(qǐng)");
}
@end
3、測試
handler *handler1 = [projectManagerHandler new];
handler *handler2 = [depManagerHandler new];
handler *handler3 = [generalManagerHandler new];
//設(shè)置責(zé)任鏈中的下一個(gè)處理對(duì)象
handler1.successor = handler2;
handler2.successor = handler3;
[handler1 handleRequest:100];
NSLog(@"----------------------------------");
[handler1 handleRequest:700];
NSLog(@"----------------------------------");
[handler1 handleRequest:10000];
4、輸出
2016-12-14 19:48:37.405 責(zé)任鏈模式[65533:2201187] 項(xiàng)目經(jīng)理同意了費(fèi)用申請(qǐng)
2016-12-14 19:48:37.406 責(zé)任鏈模式[65533:2201187] ----------------------------------
2016-12-14 19:48:37.406 責(zé)任鏈模式[65533:2201187] 項(xiàng)目經(jīng)理沒有權(quán)限批準(zhǔn),轉(zhuǎn)到部門經(jīng)理處理
2016-12-14 19:48:37.406 責(zé)任鏈模式[65533:2201187] 部門經(jīng)理同意了費(fèi)用申請(qǐng)
2016-12-14 19:48:37.406 責(zé)任鏈模式[65533:2201187] ----------------------------------
2016-12-14 19:48:37.406 責(zé)任鏈模式[65533:2201187] 項(xiàng)目經(jīng)理沒有權(quán)限批準(zhǔn),轉(zhuǎn)到部門經(jīng)理處理
2016-12-14 19:48:37.406 責(zé)任鏈模式[65533:2201187] 部門經(jīng)理沒有權(quán)限批準(zhǔn),轉(zhuǎn)到總經(jīng)理處理
2016-12-14 19:48:37.406 責(zé)任鏈模式[65533:2201187] 總經(jīng)理同意了費(fèi)用申請(qǐng)
Program ended with exit code: 0
假設(shè)這個(gè)時(shí)候申請(qǐng)流程變了,如下所示:
- 費(fèi)用小于1000,由項(xiàng)目經(jīng)理審批
- 費(fèi)用大于1000,由總經(jīng)理審批
流程里面去掉了部門經(jīng)理,并且每個(gè)經(jīng)理的審批金額也變了,那么如何更改呢?
很簡單,分為兩步
- 更改原來項(xiàng)目經(jīng)理和總經(jīng)理的審批流程
- 改變責(zé)任鏈,如下所示
handler1.successor = handler2;
handler2.successor = handler3;
改為:
handler1.successor = handler3;
客戶端計(jì)算獎(jiǎng)金的方式依然不變,這就是責(zé)任鏈的強(qiáng)大之處,可以動(dòng)態(tài)選擇構(gòu)成責(zé)任鏈的成員,看到這里是不是覺得和裝飾者模式很類似?裝飾器模式也可以動(dòng)態(tài)的選擇給原有對(duì)象添加各種功能,他們?cè)谀撤N程度上是可以互相替換的,兩者都可以實(shí)現(xiàn)動(dòng)態(tài)給對(duì)象添加功能。
標(biāo)準(zhǔn)的職責(zé)鏈模式是鏈中有一個(gè)對(duì)象能處理請(qǐng)求,就停止把請(qǐng)求往下傳遞,如果不停止傳遞,那么此時(shí)的職責(zé)鏈模式就和裝飾器模式很類似了,每個(gè)處理對(duì)象都相當(dāng)于一個(gè)裝飾器。但是他們的目的值不同的,裝飾器是為了透明的給對(duì)象添加功能,而職責(zé)鏈?zhǔn)菫榱私怦钫?qǐng)求的發(fā)送者和接受者
優(yōu)缺點(diǎn)
-
降低耦合度
該模式使得一個(gè)對(duì)象無需知道是其他哪一個(gè)對(duì)象處理其請(qǐng)求。對(duì)象僅需
知道該請(qǐng)求會(huì)被“正確”地處理。接收者和發(fā)送者都沒有對(duì)方的明確的信息,且鏈中的對(duì)象不需知道鏈的結(jié)構(gòu)。
結(jié)果是,職責(zé)鏈可簡化對(duì)象的相互連接。它們僅需保持一個(gè)指向其后繼者的引用,而不需保持它所有的候選接受者的引用。
-
動(dòng)態(tài)組合職責(zé)
當(dāng) 在 對(duì) 象 中 分 派 職 責(zé) 時(shí) , 職 責(zé) 鏈 給 你 更多的靈活性。你可以通過在運(yùn)行時(shí)刻對(duì)該鏈進(jìn)行動(dòng)態(tài)的增加或修改,來增加或改變處理一個(gè)請(qǐng)求的職責(zé)鏈對(duì)象。
-
不保證被接受
既然一個(gè)請(qǐng)求沒有明確的接收者,那么就不能保證它一定會(huì)被處理。 該請(qǐng)求可能一直到鏈的末端都得不到處理。一個(gè)請(qǐng)求也可能因該鏈沒有被正確配置而得不到處理。
處理多個(gè)請(qǐng)求的職責(zé)鏈
上面的職責(zé)鏈中的每個(gè)對(duì)象(各個(gè)經(jīng)理)都只處理一種請(qǐng)求,實(shí)際情況每個(gè)對(duì)象可能需要處理多種業(yè)務(wù)類型。比如現(xiàn)在又增加了差旅費(fèi)用申請(qǐng)。
常規(guī)做法是在抽象類handler中增加一個(gè)新的方法,然后在每個(gè)具體職責(zé)鏈對(duì)象中去實(shí)現(xiàn)這個(gè)方法,但是這樣做就違反了開閉原則,導(dǎo)致每次增加或者修改業(yè)務(wù),都需要修改職責(zé)鏈中的每個(gè)對(duì)象。有沒有一種通用的處理辦法來處理所有的業(yè)務(wù)呢?肯定有,具體見demo,這里就不展開說了。
功能鏈
其實(shí)在實(shí)際開發(fā)中一般都會(huì)對(duì)職責(zé)鏈進(jìn)行變通使用,標(biāo)準(zhǔn)的職責(zé)鏈實(shí)現(xiàn)是:如果鏈中對(duì)象可以處理請(qǐng)求,就停止把請(qǐng)求傳遞到下一個(gè)對(duì)象。變通的處理就是不停止傳遞,每個(gè)對(duì)象都參與處理請(qǐng)求,這樣的例子很多。
比如各種web過濾器,一個(gè)連接請(qǐng)求進(jìn)來,會(huì)進(jìn)行權(quán)限檢查,字符集轉(zhuǎn)換等過濾,可以被每個(gè)過濾功能都實(shí)現(xiàn)為一個(gè)職責(zé)鏈對(duì)象,然后對(duì)于不同的請(qǐng)求就可以動(dòng)態(tài)組合這些職責(zé)鏈對(duì)象來構(gòu)成一條職責(zé)鏈進(jìn)行過濾。
再比如用戶的注冊(cè)流程一般如下圖所示:
藍(lán)色方塊代表的是一個(gè)個(gè)的流程,每個(gè)箭頭流向都是一條鏈,我們可以把每個(gè)流程時(shí)限為職責(zé)鏈對(duì)象,然后根據(jù)不同的流程組合出來不同的職責(zé)鏈來處理注冊(cè)。
這里只是做拋磚引玉之用,具體代碼就不演示了。職責(zé)鏈在實(shí)際開發(fā)過程中用途廣泛,只要是流程類的過程都可以嘗試使用職責(zé)鏈去實(shí)現(xiàn)。