看图识模式
我们以工作中的一个流程来举例,虽然已经被用滥了,但是我觉得它对今天讲的这个模式的使用恰到好处。
比如公司中的某个员工小A最近表现很好,工作努力、解决问题效率高,所以小A想提出涨工资的要求。但是公司的管理序列负责,该怎么办?
首先,小A找到了直属部门经理,但是部门经理说我没有权限。
接下来,小A去找部门总监,同样部门总监也说没有权限;
最后,小A辗转找到了总经理,总经理经过认真考察,同意了A的请求。
分析
在上面的处理过程中有个问题: 员工小A需要知道多个管理者,知道这个管理序列的每一个人。并且需要大量的结果判断,这就产生了耦合
。
或则我们不设置那么多管理者,只有一个不就行了,让它处理所有的管理问题。现实中肯定不行(能累死~),但是代码跑起来还是没有问题的。但是,这样就不符合单一职责
,而且添加新的管理功能还需要改写这个类,也不符合开放-封闭原则
。
通过上面的情况,我们可以总结出2个规律:
- 处理过程是一个链条,需要每个管理者一级一级的处理。
- 一旦某个管理者能够处理这个问题,请求就结束了,不需要再找下一个管理者。
解决
我们可以这样设计,让每一个管理者添加一个下一处理者(nextHandler)的指向,这个指向就是他的上级。当他接受到一个请求(request)的时候,他会判断自己有没有权限处理,如果没有权限就把这个请求传递给下一个处里者(nextHandler),如果有权限就处理掉这个事情,然后结束传递。后面的每一个处理者都同样重复这样的操作。
管理者的关系就是这样:
问题的处理流程是这样:
其实现实工作流程或则线上bpm管理都是使用了这种方式,它就是责任链模式
。
模式定义
定义
责任链模式: 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间发生耦合。此模式将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
责任链模式的主要思想是,对象引用了同一类型的另一个对象,形成一条链。链中的每个对象实现了同样的方法,处理对链中第一个对象发起的同一个请求。如果一个对象不知道如何处理请求,它就把请求传递给下一个响应者
类结构图
责任链模式包含如下角色:
Handler(抽象处理者):定义一个处理请求的接口,一般设计为抽象类。因为每一个处理者的下家还是一个处理者,因此在抽象处理者中定义了一个抽象处理者类型的对象作为其对下家的引用。通过该引用,处理者可以连成一条链。
ConcreteHandler(具体处理者): 具体处理者类中实现了抽象处理者中定义的抽象请求处理方法,在处理请求之前需要进行判断,看是否有相应的处理权限,如果可以处理请求就处理它,否则将请求转发给后继者;
代码
根据上面的定义,看一下代码中如何实现:
先写一个请求类,这个类就是后续在具体处理者中需要处理的对象,是依据你的业务来编写的,没有固定的格式;这里写个示例
@interface Request : NSObject
@property (nonatomic, copy)NSString *type;
@property (nonatomic, assign)NSUInteger number;
@end
@implementation Request
@end
Handler(抽象处理者),这个例子里对应的是管理者Manager
@interface Manager : NSObject
@property (nonatomic, strong) Manager *nextManager;
- (void)handleRequest:(Request *)quest;
@end
@implementation Manager
- (void)handleRequest:(Request *)quest
{
[self.nextManager handleRequest:quest];
}
@end
抽象管理者中有一个对下级处理者的引用,默认的处理方法是把请求转发给下一级处理者。
ConcreteHandler(具体处理者): 具体处理者对应的有3个,分别是部门经理、部门总监、总经理。
部门经理类,可以处理小于5天以内的请假
@interface DepartmentManager : Manager
@end
@implementation DepartmentManager
- (void)handleRequest:(Request *)quest
{
if ([quest.type isEqualToString:@"请假"] && quest.number < 5) {
NSLog(@"%@:处理了 %@,%@", NSStringFromClass([self class]), quest.type, @(quest.number));
} else {
NSLog(@"%@:无权处理 %@,%@", NSStringFromClass([self class]), quest.type, @(quest.number));
[self.nextManager handleRequest:quest];
}
}
@end
部门总监类,可以处理大于5天的请假和调岗
@interface DepartmentDirector : Manager
@end
@implementation DepartmentDirector
- (void)handleRequest:(Request *)quest
{
if ([quest.type isEqualToString:@"调岗"] || ([quest.type isEqualToString:@"请假"] && quest.number > 5)) {
NSLog(@"%@:处理了 %@,%@", NSStringFromClass([self class]), quest.type, @(quest.number));
} else {
NSLog(@"%@:无权处理 %@,%@", NSStringFromClass([self class]), quest.type, @(quest.number));
[self.nextManager handleRequest:quest];
}
}
@end
总经理类:可以处理5000以内的涨薪请求
@interface GeneralManager : Manager
@end
@implementation GeneralManager
- (void)handleRequest:(Request *)quest
{
if ([quest.type isEqualToString:@"涨薪"] && quest.number < 5000) {
NSLog(@"%@:处理了 %@,%@", NSStringFromClass([self class]), quest.type, @(quest.number));
} else {
NSLog(@"%@:无权处理 %@,%@", NSStringFromClass([self class]), quest.type, @(quest.number));
[self.nextManager handleRequest:quest];
}
}
@end
客户端调用
DepartmentManager *departmentManager = [[DepartmentManager alloc] init];
DepartmentDirector *departmentDirector = [[DepartmentDirector alloc] init];
GeneralManager *generalManager = [[GeneralManager alloc] init];
departmentManager.nextManager = departmentDirector;
departmentDirector.nextManager = generalManager;
Request *quest1 = [[Request alloc] init];
quest1.type = @"涨薪";
quest1.number = 1000;
[departmentManager handleRequest:quest1];
Request *quest2 = [[Request alloc] init];
quest2.type = @"请假";
quest2.number = 10;
[departmentManager handleRequest:quest2];
运行结果
员工发出请求后就不需要关心后续处理请求的传递和处理了。而请求最终也会流转到能够处理它的管理者手中得到处理。
职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
这里发出这个请求的客户端并不知道这当中的哪一个对象最终处理这个请求,这样系统的更改可以在不影响客户端的情况下动态地重新组织和分配责任。
比如公司的管理序列发生变化,增加了一个总裁,我们只需要在链条中加上这个管理者即可,
而不用关心请求发起方。这个结构完全就是一个单向链表的结构,同样它也有增加、删除的动态灵活性。
特征
灵活性
职责链灵活在哪
-
改变内部的传递规则
在内部,项目经理完全可以跳过人事部到那一关直接找到总经理。每个人都可以去动态地指定他的继任者。
-
可以从职责链任何一关开始。
如果项目经理不在,可以直接去找部门经理,责任链还会继续,没有影响。
-
用与不用的区别
不用职责链的结构,我们需要和公司中的每一个层级都发生耦合关系。 如果反映在代码上即使我们需要在一个类中去写上很多丑陋的if….else语句。 如果用了职责链,相当于我们面对的是一个黑箱,我们只需要认识其中的一个部门, 然后让黑箱内部去负责传递就好了
目的
避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
使用场景
- 有多个对象可以处理请求,而处理程序只有在运行时才能确定。
- 向一组对象发出请求,而不想显示指定处理请求的特定处理程序。
优缺点
优点
- 职责链模式使得一个对象无须知道是其他哪一个对象处理其请求,对象仅需知道该请求会被处理即可,接收者和发送者都没有对方的明确信息,且链中的对象不需要知道链的结构,由客户端负责链的创建,降低了系统的耦合度。
- 请求处理对象仅需维持一个指向其后继者的引用,而不需要维持它对所有的候选处理者的引用,可简化对象的相互连接。
- 在给对象分派职责时,职责链可以给我们更多的灵活性,可以通过在运行时对该链进行动态的增加或修改来增加或改变处理一个请求的职责。
- 在系统中增加一个新的具体请求处理者时无须修改原有系统的代码,只需要在客户端重新建链即可,从这一点来看是符合“开闭原则”的。
缺点
- 因为一个请求没有明确的接收者,该请求可能一直到链的末端都得不到处理;一个请求也可能因职责链没有被正确配置而得不到处理。
- 对于比较长的职责链,请求的处理可能涉及到多个处理对象,系统性能将受到一定影响,而且在进行代码调试时不太方便。
- 如果建链不当,可能会造成循环调用,将导致系统陷入死循环。