一臭家、責(zé)任鏈模式介紹
責(zé)任鏈模式:將能夠處理同一類請(qǐng)求的對(duì)象連成一條鏈呵恢,使這些對(duì)象都有機(jī)會(huì)處理請(qǐng)求种蘸,所提交的請(qǐng)求沿著鏈傳遞墓赴。從而避免請(qǐng)求的發(fā)送者和接受者之間的耦合關(guān)系。鏈上的對(duì)象逐個(gè)判斷是否有能力處理該請(qǐng)求航瞭,如果能則就處理诫硕,如果不能,則傳給鏈上的下一個(gè)對(duì)象刊侯。直到有一個(gè)對(duì)象處理它為止章办。
生活中場(chǎng)景:
1、打牌時(shí)滨彻,輪流出牌
2藕届、接力賽跑
3、請(qǐng)假審批
4疮绷、公文審批
責(zé)任鏈UML圖:
角色說明:
Handler(抽象處理者):抽象類或者接口,定義處理請(qǐng)求的方法以及持有下一個(gè)Handler的引用.
ConcreteHandler1,ConcreteHandler2(具體處理者):實(shí)現(xiàn)抽象處理類,對(duì)請(qǐng)求進(jìn)行處理,如果不處理則轉(zhuǎn)發(fā)給下一個(gè)處理者.
Client (客戶端):即要使用責(zé)任鏈模式的地方翰舌。
二、責(zé)任鏈模式代碼實(shí)現(xiàn)
這里以請(qǐng)假的流程為例冬骚,用責(zé)任鏈模式來實(shí)現(xiàn)
首先這里定義一個(gè)請(qǐng)假信息的對(duì)象
// 請(qǐng)假的基本信息類
// 請(qǐng)假的基本信息類
#import <Foundation/Foundation.h>
@interface LeaveRequest : NSObject
/** 請(qǐng)假人 */
@property (nonatomic, strong, readonly) NSString *empName;
/** 請(qǐng)假天數(shù) */
@property (nonatomic, assign, readonly) NSInteger leaveDays;
/** 請(qǐng)假理由 */
@property (nonatomic, strong, readonly) NSString *reason;
/**
* 全量初始化方法
*
* @param empName 請(qǐng)假人姓名
* @param leaveDays 請(qǐng)假天數(shù)
* @param reson 請(qǐng)假理由
* @return 本實(shí)例
*/
- (instancetype)initWithEmpName:(NSString *)empName
leaveDays:(NSInteger)leaveDays
reson:(NSString *)reson;
@end
#import "LeaveRequest.h"
@interface LeaveRequest()
/** 請(qǐng)假人 */
@property (nonatomic, strong) NSString *empName;
/** 請(qǐng)假天數(shù) */
@property (nonatomic, assign) NSInteger leaveDays;
/** 請(qǐng)假理由 */
@property (nonatomic, strong) NSString *reason;
@end
@implementation LeaveRequest
- (instancetype)initWithEmpName:(NSString *)empName
leaveDays:(NSInteger)leaveDays
reson:(NSString *)reson {
if (self = [super init]) {
self.empName = empName;
self.leaveDays = leaveDays;
self.reason = reson;
}
return self;
}
@end
然后定義一個(gè)抽象類(iOS中使用接口)椅贱,來處理各個(gè)請(qǐng)求之間的關(guān)系懂算。也就是UML圖中的Handler部分
// 抽象類(接口):管理責(zé)任鏈上對(duì)象的共同行為
#import <Foundation/Foundation.h>
#import "LeaveRequest.h"
@protocol LeaveResponseInterface <NSObject>
/** 下一個(gè)請(qǐng)假處理者 */
@property (nonatomic, strong) id<LeaveResponseInterface> nextLeaveResponser;
/** 請(qǐng)求處理方法 */
- (BOOL)handleLeaveRequest:(LeaveRequest *)leaveReauest;
@end
接下來就可以開始定義處理請(qǐng)求的具體對(duì)象了,比如處理請(qǐng)假信息的:主任庇麦,經(jīng)理计技,總經(jīng)理等等。這些對(duì)象都必須繼承抽象類山橄,來處理請(qǐng)求垮媒。
主任對(duì)象:處理小于等于3天的假期
// 主任
#import <Foundation/Foundation.h>
#import "LeaveResponseInterface.h"
@interface Director : NSObject <LeaveResponseInterface>
/** 角色名稱 */
@property (nonatomic, strong, readonly) NSString *name;
// 實(shí)現(xiàn)協(xié)議:LeaveResponseInterface
@property (nonatomic, strong) id<LeaveResponseInterface> nextLeaveResponser;
- (instancetype)initWithName:(NSString *)name;
// 實(shí)現(xiàn)協(xié)議:LeaveResponseInterface
- (BOOL)handleLeaveRequest:(LeaveRequest *)leaveReauest;
@end
#import "Director.h"
@interface Director()
@property (nonatomic, strong) NSString *name;
@end
@implementation Director
- (instancetype)initWithName:(NSString *)name {
if (self = [super init]) {
self.name = name;
}
return self;
}
- (BOOL)handleLeaveRequest:(LeaveRequest *)leaveReauest {
if (leaveReauest.leaveDays < 3) {
NSLog(@"請(qǐng)假人:%@, 天數(shù):%ld, 理由:%@", leaveReauest.empName, leaveReauest.leaveDays, leaveReauest.reason);
NSLog(@"審批人:%@主任,審批通過航棱!", self.name);
return YES;
} else {
// 下一個(gè)審批者進(jìn)行處理
if (self.nextLeaveResponser) {
return [self.nextLeaveResponser handleLeaveRequest:leaveReauest];
} else {
return NO;
}
}
}
@end
以下經(jīng)理和總經(jīng)理類在實(shí)際開發(fā)中睡雇,不需要繼承自Director而是直接實(shí)現(xiàn)協(xié)議。這里使用繼承只是Demo的場(chǎng)景比較簡(jiǎn)單饮醇,便于復(fù)用代碼它抱。純屬省力之舉,并不科學(xué)朴艰。
經(jīng)理對(duì)象:處理大于3天观蓄,小于等于5天的假期
// 經(jīng)理
#import <Foundation/Foundation.h>
#import "Director.h"
@interface Manager : Director
@end
#import "Manager.h"
@implementation Manager
- (BOOL)handleLeaveRequest:(LeaveRequest *)leaveReauest {
if (leaveReauest.leaveDays <= 5 && leaveReauest.leaveDays >= 3) {
NSLog(@"請(qǐng)假人:%@, 天數(shù):%ld, 理由:%@", leaveReauest.empName, leaveReauest.leaveDays, leaveReauest.reason);
NSLog(@"審批人:%@經(jīng)理,審批通過!", self.name);
return YES;
} else {
// 下一個(gè)審批者進(jìn)行處理
if (self.nextLeaveResponser) {
return [self.nextLeaveResponser handleLeaveRequest:leaveReauest];
} else {
return NO;
}
}
}
@end
總經(jīng)理對(duì)象:處理大于5天祠墅,小于15天的請(qǐng)假信息
// 總經(jīng)理
#import "Director.h"
@interface GeneralManager : Director
@end
#import "GeneralManager.h"
@implementation GeneralManager
- (BOOL)handleLeaveRequest:(LeaveRequest *)leaveReauest {
if (leaveReauest.leaveDays > 5 && leaveReauest.leaveDays < 15) {
NSLog(@"請(qǐng)假人:%@, 天數(shù):%ld, 理由:%@", leaveReauest.empName, leaveReauest.leaveDays, leaveReauest.reason);
NSLog(@"審批人:%@總經(jīng)理侮穿,審批通過!", self.name);
return YES;
} else {
// 下一個(gè)審批者進(jìn)行處理
if (self.nextLeaveResponser) {
return [self.nextLeaveResponser handleLeaveRequest:leaveReauest];
} else {
return NO;
}
}
}
@end
重要代碼都寫完了,下面開始測(cè)試:
// 構(gòu)建各領(lǐng)導(dǎo)人
Director *a = [[Director alloc] initWithName:@"張三"];
Manager *b = [[Manager alloc] initWithName:@"李四"];
GeneralManager *c = [[GeneralManager alloc] initWithName:@"王五"];
// 設(shè)置責(zé)任鏈關(guān)系
a.nextLeaveResponser = b;
b.nextLeaveResponser = c;
// 開始請(qǐng)假
LeaveRequest *leaveRequest = [[LeaveRequest alloc] initWithEmpName:@"小明" leaveDays:3 reson:@"旅游"];
[a handleLeaveRequest:leaveRequest];
控制臺(tái)則打踊汆隆:主任審批
請(qǐng)假人:小明,天數(shù):3,理由:旅游
審批人:張三 主任亲茅,審批通過!
如果改成13天:則就是總經(jīng)理審批
請(qǐng)假人:小明,天數(shù):13,理由:旅游
審批人:王五 總經(jīng)理金矛,審批通過芯急!
三、責(zé)任鏈模式總結(jié)
一句話定義:
當(dāng)你想讓多個(gè)同類對(duì)象都有機(jī)會(huì)處理某個(gè)請(qǐng)求的時(shí)候就可以使用責(zé)任鏈模式
實(shí)現(xiàn)方式:
1驶俊、鏈表方式:比如剛才的請(qǐng)假審批
2、非鏈表方式:通過集合免姿,數(shù)組生成責(zé)任鏈更加實(shí)用饼酿,將鏈表上的各個(gè)對(duì)象都添加到集合中,然后通過反射給構(gòu)建出來胚膊。
然后在容器里一個(gè)個(gè)的處理故俐。(也就是說把測(cè)試代碼中除了請(qǐng)假的其他代碼都給用一個(gè)類來處理)
開發(fā)中常見場(chǎng)景:
1、iOS紊婉、Android 事件分發(fā)機(jī)制
2药版、Java的異常機(jī)制就是一個(gè)責(zé)任鏈模式,一個(gè)try可以對(duì)應(yīng)多個(gè)cathc喻犁。如果某一個(gè)catch不匹配槽片,則跳到下一個(gè)catch中
3何缓、JavaScript語言中的事件的冒泡和捕獲機(jī)制
4、Servlet開發(fā)中还栓,過濾器的鏈?zhǔn)教幚?/p>
5碌廓、Struts2中,攔截器的調(diào)用也是典型的責(zé)任鏈模式
責(zé)任鏈的好處:
1剩盒、接受者和發(fā)送者及接受者之間解耦谷婆。接受者和發(fā)送者都沒有對(duì)方的明確信息,且鏈中的對(duì)象也并不知道鏈的結(jié)構(gòu)辽聊,結(jié)果是責(zé)任鏈可簡(jiǎn)化對(duì)象的相互連接纪挎,它們僅需保持一個(gè)指向其后繼者的引用,而不需要保持它所有的候選繼承者跟匆,大大的降低了耦合度廷区。
發(fā)送者不用管具體哪個(gè)對(duì)象會(huì)處理,反正該請(qǐng)求肯定會(huì)被處理就行了
2贾铝、可以隨時(shí)增加或者修改處理一個(gè)請(qǐng)求的結(jié)構(gòu)隙轻,增加了給對(duì)象指派職責(zé)的靈活性
責(zé)任鏈的弊端:
1、請(qǐng)求有可能遍歷完鏈都得不到處理
2垢揩、不容易觀察運(yùn)行時(shí)的特征玖绿,有礙于debug