設(shè)計(jì)模式系列--職責(zé)鏈模式(Chain of Responsibility)

一臭家、責(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圖:

Chain of Responsibility Pattern UML.jpg
角色說明:
  • 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

Demo 下載鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市叁巨,隨后出現(xiàn)的幾起案子斑匪,更是在濱河造成了極大的恐慌,老刑警劉巖锋勺,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚀瘸,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡庶橱,警方通過查閱死者的電腦和手機(jī)贮勃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來苏章,“玉大人寂嘉,你說我怎么就攤上這事》闵穑” “怎么了泉孩?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)并淋。 經(jīng)常有香客問我寓搬,道長(zhǎng),這世上最難降的妖魔是什么县耽? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任句喷,我火速辦了婚禮镣典,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘脏嚷。我一直安慰自己骆撇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布父叙。 她就那樣靜靜地躺著神郊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪趾唱。 梳的紋絲不亂的頭發(fā)上涌乳,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天,我揣著相機(jī)與錄音甜癞,去河邊找鬼夕晓。 笑死,一個(gè)胖子當(dāng)著我的面吹牛悠咱,可吹牛的內(nèi)容都是我干的蒸辆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼析既,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼躬贡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起眼坏,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤拂玻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后宰译,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體檐蚜,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年沿侈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了闯第。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡肋坚,死狀恐怖乡括,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情智厌,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布盲赊,位于F島的核電站铣鹏,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏哀蘑。R本人自食惡果不足惜诚卸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一葵第、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧合溺,春花似錦卒密、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至睛约,卻和暖如春鼎俘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辩涝。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工贸伐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人怔揩。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓捉邢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親商膊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子伏伐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容