IOS设计模式之责任链模式

转载:http://my.oschina.net/daguoshi/blog/495573
本篇文章参照Objective-C编程之道,iOS设计模式解析一书(Carlo Chung)而来的,参考了其大量的实例与思想,通过写博客的方式,让自己对这些iOS中常见的设计模式做一个总结,以加深自己对设计模式的理解与应用。希望能够对大家有点帮助。

何为责任链模式?

    责任链模式的主要思想是,对象引用了同一类型的另一个对象,形成一条链。链中的每个对象实现了同样的方法,处理对链中第一个对象发起的同一个请求。如果一个对象不知道如何处理请求,它就把请求传递给下一个响应者。

    责任链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间发生耦合。此模式将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

何时使用责任链模式?

    @:有多个对象可以处理请求,而处理程序只有在运行时才能确定。

    @:向一组对象发出请求,而不想显示指定处理请求的特定处理程序。

在游戏中使用责任链模式

    假定我们要开发一款游戏,里面的每个人物都可以通过做任务赚取点数来升级防御道具。防御道具可以使盾牌或者盔甲。每种形式的防御只能应付一种特定的攻击,如果防御道具不认识一种进攻,它就把进攻的作用传递给下一个会响应它的实体。比如,盔甲1不知道如何对付对手的攻击,所以把它传给下一个盔甲,盔甲2。盔甲2刚好知道如何对付这次攻击,化解了人物可能受到的损伤。由于某种原因,如果没有盔甲可以对这次攻击做出响应,攻击的作用最终会传到人物。人物对攻击做出响应时,会表现为一定程度的损伤。

    这种只让每个独立的防御道具对特定类型的攻击做出相应的机制,简化了人物使用各种防御道具的复杂性。每种盔甲各自负责非常特定的功能。这就是责任链模式的作用所在。

    下面我们将使用责任链模式实现这个设计,假设有两种防御:水盔甲和火盔甲。它们都只能按照设计对付某些攻击。水盔甲可以防御来自水的攻击,火盔甲可以防御来自火的攻击。人物也是响应链的一部分,因此它也应该跟其他防御道具具有共同的行为,对攻击做出响应。

    WaterAttackHandler、FireAttackHandler和Avatar是AttackHandler的子类。AttackHandler定义了一个方法——handleAttack:attack,该方法的默认行为是,把攻击传给另一个AttackHandler的引用,即成员变量nextAttackHandler。子类重载这个方法,对攻击提供实际的响应。如果AttackHandler不知道如何响应一个攻击,那么就使用[super handleAttack:attack];消息,把它转发给super,这样super的默认实现就会把攻击沿着链给传下去。

    定义3中类型的攻击,WaterAttack、FireAttack、SoliderAttack。先看下AttackHandler父类的代码,代码如下:

?
1
2
3
4
5
6
7
8
9
#import <Foundation/Foundation.h>
#import "Attack.h"
@interface AttackHandler : NSObject
 
@property (nonatomic, strong) AttackHandler *nextAttackHandler;
 
- ( void )handleAttack:(Attack *)attack;
 
@end

    AttackHandler定义了一个同类型的私有变量nextAttackHandler,它是攻击的下一个响应者。AttackHander的子类应该重载handleAttack:方法,以响应它能够识别的一种攻击。抽象的AttackHandler为这个方法定义了默认行为,代码如下:

?
1
2
3
4
5
6
7
8
9
10
#import "AttackHandler.h"
 
@implementation AttackHandler
 
- ( void )handleAttack:(Attack *)attack {
     // 默认调用nextAttackHandler进行处理。
     [_nextAttackHandler handleAttack:attack];
}
 
@end

    如果子类没有重载这个方法,默认的handleAttack:实现就会被调用。这个方法只是把攻击传给nextAttackHandler去处理。

    接下来看下任务的第一个防御道具WaterAttackHandler,WaterAttackHandler子类化AttackHandler并重载其handleAttack:方法,代码如下:

?
1
2
3
4
5
6
7
8
#import "AttackHandler.h"
 
@interface WaterAttackHandler : AttackHandler
 
// 重写处理攻击方法
- ( void )handleAttack:(Attack *)attack;
 
@end

    在.h中再次声明重载的方法不是必需的,但是这样做更加清晰。WaterAttackHandler只能识别WaterAttack的实例,如果攻击确实是WaterAttack类型,那么handleAttack:将用NSLog输出@"我挡下了水的攻击";否则,它输出另一条消息并使用[super handleAttack:attack];把攻击转给super,代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#import "WaterAttackHandler.h"
#import "WaterAttack.h"
@implementation WaterAttackHandler
 
- ( void )handleAttack:(Attack *)attack {
     if  ([attack isKindOfClass:[WaterAttack  class ]]) {
         NSLog(@ "我挡下了水的攻击" );
     } else  {
         NSLog(@ "我处理不了来自%@的攻击" , [attack  class ]);
         [super handleAttack:attack];
     }
}
 
@end

    类似的FireAttackHandler也是同样的道理,代码如下:

?
1
2
3
4
5
6
7
8
#import "AttackHandler.h"
 
@interface FireAttackHandler : AttackHandler
 
// 重写处理攻击的方法
- ( void )handleAttack:(Attack *)attack;
 
@end
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#import "FireAttackHandler.h"
#import "FireAttack.h"
@implementation FireAttackHandler
 
- ( void )handleAttack:(Attack *)attack {
     if  ([attack isKindOfClass:[FireAttack  class ]]) {
         NSLog(@ "我挡下了火的攻击" );
     } else  {
         NSLog(@ "我处理不了来自%@的攻击" , [attack  class ]);
         [super handleAttack:attack];
     }
}
 
@end

    如果没有防具能够对付攻击,攻击最终将传给Avatar(游戏人物)。Avatar也是AttackHandler的子类,而且与WaterAttackHandler、FireAttackHandler有相同的响应机制。但是,攻击到这里的时候,Avatar将没有防御而受到损伤。将会输出NSLog(@"我被%@攻击到了", [attack class]);,输出攻击的名称。代码如下:

?
1
2
3
4
5
6
7
#import "AttackHandler.h"
 
@interface Avatar : AttackHandler
 
- ( void )handleAttack:(Attack *)attack;
 
@end
?
1
2
3
4
5
6
7
8
9
#import "Avatar.h"
 
@implementation Avatar
 
- ( void )handleAttack:(Attack *)attack {
     NSLog(@ "我被%@攻击到了" , [attack  class ]);
}
 
@end

    现在已经定义好了所有的AttackHandler。来看下客户端代码,看看下责任链模式的使用,客户端代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#import "ViewController.h"
#import "Attack.h"
#import "WaterAttack.h"
#import "FireAttack.h"
#import "SoliderAttack.h"
#import "AttackHandler.h"
#import "WaterAttackHandler.h"
#import "FireAttackHandler.h"
#import "Avatar.h"
@interface ViewController ()
 
@end
 
@implementation ViewController
 
#pragma mark - life cycle
 
- ( void )viewDidLoad {
     [super viewDidLoad];
     
     // 创建一个游戏人物
     AttackHandler *avatar = [[Avatar alloc] init];
     
     // 给其穿上防水的铠甲
     AttackHandler *waterAramedAvatar = [[WaterAttackHandler alloc] init];
     [waterAramedAvatar setNextAttackHandler:avatar];
     
     // 然后在穿上防火的铠甲
     AttackHandler *fireAramedAvatar = [[FireAttackHandler alloc] init];
     [fireAramedAvatar setNextAttackHandler:waterAramedAvatar];
     
     // ....以后还可以加其他行动
     
     // 用用水攻击游戏人物
     Attack *waterAttack = [[WaterAttack alloc] init];
     [fireAramedAvatar handleAttack:waterAttack];
     
     // 用火攻击游戏人物
     Attack *fireAttack = [[FireAttack alloc] init];
     [fireAramedAvatar handleAttack:fireAttack];
     
     // 用土攻击游戏人物
     Attack *soliderAttack = [[SoliderAttack alloc] init];
     [fireAramedAvatar handleAttack:soliderAttack];
     
     // ....以后可以加其他的攻击
}
 
- ( void )didReceiveMemoryWarning {
     [super didReceiveMemoryWarning];
     // Dispose of any resources that can be recreated.
}
 
@end

    这个攻击有点像栈(先进后出)。因为需要让Avatar是攻击的最后一站,所以它要最先创建。然后创WaterAttackHandler的实例,把Avatar作为它的下一个AttackHandler。它们被当做增强了Avatar,WaterAttackHandler是它通往真正Avatar实例的第一道门。然后,添加FireAttackHandler作为Avatar的另一种防御。此时,Avatar已经具有两种防御道具了。

    在游戏中的某个时刻,我们创建了3中类型的攻击——waterAttack、fireAttack、soliderAttack,调用handleAttack:方法去处理,以下是来自责任链中各种AttackHandler的输出,客户端输出如下:

?
1
2
3
4
5
6
2015-08-21 14:57:31.922 ChainResponsibilityDemo[25726:1690159] 我处理不了来自WaterAttack的攻击
2015-08-21 14:57:31.923 ChainResponsibilityDemo[25726:1690159] 我挡下了水的攻击
2015-08-21 14:57:31.923 ChainResponsibilityDemo[25726:1690159] 我挡下了火的攻击
2015-08-21 14:57:31.923 ChainResponsibilityDemo[25726:1690159] 我处理不了来自SoliderAttack的攻击
2015-08-21 14:57:31.923 ChainResponsibilityDemo[25726:1690159] 我处理不了来自SoliderAttack的攻击
2015-08-21 14:57:31.924 ChainResponsibilityDemo[25726:1690159] 我被SoliderAttack攻击到了

    这个例子演示了如何使用责任链模式,来简化人物处理各种攻击的编码和逻辑。如果不用这个模式,防御逻辑很可能都塞到一个类中(比如Avatar),代码会乱成一团的。


demo地址:

https://github.com/guoshimeihua/ChainResponsibilityDemo

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值