項目做完了食寡,作為老大的你決定叫上項目組的人一起聚個餐犒勞下大家拟烫,于是你去財務申請聚餐費用,財務告訴你聚餐費用申請的流程如下:
- 小于500元赠摇,項目經(jīng)理可以審批
- 大于500小于1000超燃,需要部門經(jīng)理審批
- 大于1000的都需要總經(jīng)理審批
如果讓你來實現(xiàn)這個流程区拳,該如何做呢?常規(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(@"項目經(jīng)理同意了費用申請");
}
-(void)depManagerHandle{
NSLog(@"部門經(jīng)理同意了費用申請");
}
-(void)generalManagerHandle{
NSLog(@"總經(jīng)理同意了費用申請");
}
@end
實現(xiàn)起來很簡單吧樱调。但是仔細分析下,上面的寫法有如下兩個問題
申請費用的流程可能會經(jīng)常變動∏⑺玻現(xiàn)在是項目經(jīng)理-->部門經(jīng)理--->總經(jīng)理本涕,如果哪天流程倒過來呢?或者加入了新的審批的人呢伙窃?上面的代碼就無可避免的需要更改
各個審批流程可能會更改∑杏保現(xiàn)在的三個審批流程如上所示,但是可能每個經(jīng)理的審批的金額會有所更改为障,這個時候也需要更改上面的代碼
抽象下上面的流程:客戶提出一個請求(申請聚餐費用)晦闰,會有很多對象(經(jīng)理)來處理這個請求,每個對象的處理邏輯是不同的鳍怨,如果對象自己可以處理這個請求呻右,就會處理完成然后把結果返回給客戶,如果自己不能處理鞋喇,就交給其他對象處理声滥。而且希望上面的流程可以靈活變動,處理請求的對象可以隨意組合替換,來適應新的業(yè)務需求落塑。
要實現(xiàn)上述功能纽疟,可以使用職責鏈模式,下面就來具體看看
定義
使多個對象都有機會處理請求憾赁,從而避免請求的發(fā)送者和接收者之間的耦合關系污朽。將這 些對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理它為止龙考。
分析下上面的業(yè)務場景蟆肆,客戶端提取申請費用的請求,然后項目經(jīng)理晦款、部門經(jīng)理炎功、總經(jīng)理依次處理這個請求,他們組成了一個請求處理鏈(項目經(jīng)理-->部門經(jīng)理--->總經(jīng)理)缓溅,客戶請求在這個鏈中傳遞亡问,直到一個對象處理了這個請求才結束,否則一直向下傳遞到鏈的末尾肛宋,這正是職責鏈的功能。
要想讓流程的處理靈活多變束世,可以隨意組合和替換酝陈,就需要動態(tài)構建流程處理的步驟,每個步驟實現(xiàn)一個功能毁涉,然后把這些步驟串聯(lián)起來實現(xiàn)完整的請求處理鏈沉帮。
我們要實現(xiàn)發(fā)送者和接受者解耦,那么就需要讓提交幫助請求的對象不需要知道誰是最終提供幫助的對象贫堰。職責鏈模式給多個對象處理一個請求的機會,從而解耦發(fā)送者和接受者穆壕。該請 求沿對象鏈傳遞直至其中一個對象處理它,從第一個對象開始,鏈中收到請求的對象要么親自處理它,要么轉發(fā)給鏈中的下一個候 選者其屏。提交請求的對象并不明確地知道哪一個對象將會處理它喇勋,但是最終會有一個隱式對象來保證請求一定會被處理。
由上述分析可知偎行,職責鏈模式適用于如下幾種情況:
- 有多個的對象可以處理一個請求川背,但是具體是哪個對象處理該請求則是在運行時刻才確定。
- 想在不明確指定接收者的情況下蛤袒,向多個對象中的一個提交請求熄云。
- 一個請求的處理鏈可以動態(tài)設置
UML結構圖及說明
代碼實現(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妙真、實現(xiàn)三個具體的職責鏈成員
#import "handler.h"
@interface projectManagerHandler : handler
@end
=============================
#import "projectManagerHandler.h"
@implementation projectManagerHandler
-(void)handleRequest:(NSInteger)fee{
if (fee < 500) {
NSLog(@"項目經(jīng)理同意了費用申請");
}else{
if (self.successor)
NSLog(@"項目經(jīng)理沒有權限批準缴允,轉到部門經(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)理同意了費用申請");
}else{
if (self.successor)
NSLog(@"部門經(jīng)理沒有權限批準,轉到總經(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)理同意了費用申請");
}
@end
3珍德、測試
handler *handler1 = [projectManagerHandler new];
handler *handler2 = [depManagerHandler new];
handler *handler3 = [generalManagerHandler new];
//設置責任鏈中的下一個處理對象
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 責任鏈模式[65533:2201187] 項目經(jīng)理同意了費用申請
2016-12-14 19:48:37.406 責任鏈模式[65533:2201187] ----------------------------------
2016-12-14 19:48:37.406 責任鏈模式[65533:2201187] 項目經(jīng)理沒有權限批準矗漾,轉到部門經(jīng)理處理
2016-12-14 19:48:37.406 責任鏈模式[65533:2201187] 部門經(jīng)理同意了費用申請
2016-12-14 19:48:37.406 責任鏈模式[65533:2201187] ----------------------------------
2016-12-14 19:48:37.406 責任鏈模式[65533:2201187] 項目經(jīng)理沒有權限批準,轉到部門經(jīng)理處理
2016-12-14 19:48:37.406 責任鏈模式[65533:2201187] 部門經(jīng)理沒有權限批準踢俄,轉到總經(jīng)理處理
2016-12-14 19:48:37.406 責任鏈模式[65533:2201187] 總經(jīng)理同意了費用申請
Program ended with exit code: 0
假設這個時候申請流程變了缩功,如下所示:
- 費用小于1000,由項目經(jīng)理審批
- 費用大于1000都办,由總經(jīng)理審批
流程里面去掉了部門經(jīng)理嫡锌,并且每個經(jīng)理的審批金額也變了,那么如何更改呢琳钉?
很簡單势木,分為兩步
- 更改原來項目經(jīng)理和總經(jīng)理的審批流程
- 改變責任鏈,如下所示
handler1.successor = handler2;
handler2.successor = handler3;
改為:
handler1.successor = handler3;
客戶端計算獎金的方式依然不變歌懒,這就是責任鏈的強大之處啦桌,可以動態(tài)選擇構成責任鏈的成員,看到這里是不是覺得和裝飾者模式很類似及皂?裝飾器模式也可以動態(tài)的選擇給原有對象添加各種功能甫男,他們在某種程度上是可以互相替換的,兩者都可以實現(xiàn)動態(tài)給對象添加功能验烧。
標準的職責鏈模式是鏈中有一個對象能處理請求板驳,就停止把請求往下傳遞,如果不停止傳遞碍拆,那么此時的職責鏈模式就和裝飾器模式很類似了若治,每個處理對象都相當于一個裝飾器。但是他們的目的值不同的感混,裝飾器是為了透明的給對象添加功能端幼,而職責鏈是為了解耦請求的發(fā)送者和接受者
優(yōu)缺點
-
降低耦合度
該模式使得一個對象無需知道是其他哪一個對象處理其請求。對象僅需
知道該請求會被“正確”地處理弧满。接收者和發(fā)送者都沒有對方的明確的信息婆跑,且鏈中的對象不需知道鏈的結構。
結果是庭呜,職責鏈可簡化對象的相互連接洽蛀。它們僅需保持一個指向其后繼者的引用,而不需保持它所有的候選接受者的引用。
-
動態(tài)組合職責
當 在 對 象 中 分 派 職 責 時 , 職 責 鏈 給 你 更多的靈活性疟赊。你可以通過在運行時刻對該鏈進行動態(tài)的增加或修改郊供,來增加或改變處理一個請求的職責鏈對象。
-
不保證被接受
既然一個請求沒有明確的接收者,那么就不能保證它一定會被處理近哟。 該請求可能一直到鏈的末端都得不到處理驮审。一個請求也可能因該鏈沒有被正確配置而得不到處理。
處理多個請求的職責鏈
上面的職責鏈中的每個對象(各個經(jīng)理)都只處理一種請求,實際情況每個對象可能需要處理多種業(yè)務類型疯淫。比如現(xiàn)在又增加了差旅費用申請地来。
常規(guī)做法是在抽象類handler中增加一個新的方法,然后在每個具體職責鏈對象中去實現(xiàn)這個方法熙掺,但是這樣做就違反了開閉原則未斑,導致每次增加或者修改業(yè)務,都需要修改職責鏈中的每個對象币绩。有沒有一種通用的處理辦法來處理所有的業(yè)務呢蜡秽?肯定有,具體見demo缆镣,這里就不展開說了芽突。
功能鏈
其實在實際開發(fā)中一般都會對職責鏈進行變通使用,標準的職責鏈實現(xiàn)是:如果鏈中對象可以處理請求董瞻,就停止把請求傳遞到下一個對象寞蚌。變通的處理就是不停止傳遞,每個對象都參與處理請求钠糊,這樣的例子很多挟秤。
比如各種web過濾器,一個連接請求進來抄伍,會進行權限檢查煞聪,字符集轉換等過濾,可以被每個過濾功能都實現(xiàn)為一個職責鏈對象逝慧,然后對于不同的請求就可以動態(tài)組合這些職責鏈對象來構成一條職責鏈進行過濾。
再比如用戶的注冊流程一般如下圖所示:
藍色方塊代表的是一個個的流程啄糙,每個箭頭流向都是一條鏈笛臣,我們可以把每個流程時限為職責鏈對象,然后根據(jù)不同的流程組合出來不同的職責鏈來處理注冊隧饼。
這里只是做拋磚引玉之用沈堡,具體代碼就不演示了。職責鏈在實際開發(fā)過程中用途廣泛燕雁,只要是流程類的過程都可以嘗試使用職責鏈去實現(xiàn)诞丽。