声明:本系列文章内容摘自《iOS设计模式》
责任链
谁也不是无所不知,俗话说“人多智广”。每个人都有自己的专长,将每个人的智慧连成链条,链条中的每一个单元都可以为问题的解决做出贡献。如果一个人不知道如何解决问题,他就会把这个问题沿着链条传递下去,也许有人可以解决这个问题。有时候,问题即使得到解决也依然会将问题传递下去。链条允许其中的独立单元可以进行升级和改造,而不必修改已有的单元。
责任链的概念
责任链的主要思想是,对象引用了同一类型的另一对象,形成一条链。链中的每一个对象实现了同样的方法,处理对链中第一个对象发起的同一个请求。如果一个对象不知道如何处理请求,他就把请求传递给下一个相应器。
责任链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间发生耦合。此模式将这些对象连成一条链,并沿着这条链传递请求,知道有一个对象处理它为止。
何时使用责任链模式
1.有多个对象可以处理请求,而处理程序只有在运行时才能确定。
2.向一组对象发送请求,而不想显式制定处理请求的特定处理程序。
下面用RPG游戏中人物实现各种防御道具来演示该模式的使用。
在RPG游戏中使用责任链模式
假定游戏中人物有两种防具,一种是防御物理刀剑伤害的金属铠甲和防御魔法伤害的水晶盾牌,攻击包括刀枪伤害和魔法伤害。已盾牌作为第一层防御抵挡攻击,如果抵挡不了就将攻击传递给金属铠甲,金属铠甲如果也不能够抵御伤害,那么就将攻击传递到人物本身上来。用下图来描述:
图1
图2
代码实现
Avatar(人物)、MetalArmor(金属铠甲)和CrystalShield(水晶盾)是AttackHandler(攻击控制类)的子类。AttackHandler定义了一个方法——handleAttack:attack,该方法的默认行为是,把攻击传给另一个AttackHandler的引用,即成员变量nextAttackHandle_。子类重载这个方法,对攻击提供实际的响应,如果AttackHandler不知道如何响应一个攻击,那么就使用[super handleAttack:attack]消息,把攻击转发给super,这样super中的默认实现就会把攻击沿着链传递下去。
有三种攻击方式,SwordAttack(刀剑攻击)、MagicFireAttack(魔法攻击)和LightningAttack(雷电攻击),下面将讨论AttackHandler如何响应各种攻击。
首先看一下AttackHandler父类的代码实现:
#import "Attack.h" @interface AttackHandler : NSObject { @private AttackHandler *nextAttackHandler_; } @property (nonatomic, retain) AttackHandler *nextAttackHandler; -(void)handleAttack:(Attack *)attack; @end
AttackHandler定义了一个同类型的私有变量nextAttackHandler_,它是攻击的下一个响应者。AttackHandler的子类应该重载handleAttack:方法,以响应它能够识别的一种攻击。抽象的AttackHandler为这个方法定义了默认行为。
#import "AttackHandler.h" @implementation AttackHandler @synthesize nextAttackHandler = nextAttackHandler_; -(void)handleAttack:(Attack *)attack { [nextAttackHandler_ handleAttack:attack]; }
然后看一下第一个防具防具MetalArmor。
MetalArmor子类化AttackHandler并重载其中的handleAttack:方法。
#import "AttackHandler.h" @interface MetalArmor : AttackHandler //重载方法(不是必须写) -(void)handleAttack:(Attack *)attack; @end
#import "MetalArmor.h" #import "SwordAttack.h" @implementation MetalArmor -(void)handleAttack:(Attack *)attack { if ([attack isKindOfClass:[SwordAttack class]]) { NSLog(@"攻击没有通过这个金属盔甲"); } else { NSLog(@"不能识别这个攻击:%@",[attack class]); [super handleAttack:attack]; } } @end
魔法护盾同理也是这样写。
然后看一下人物(Avatar)类,它本身页是AttackHandler的子类,攻击到这里的时候就伤害到的人物本身。
#import "Avatar.h" @implementation Avatar -(void)handleAttack:(Attack *)attack { //实际损伤点数取决于攻击类型 NSLog(@"我被%@攻击到了",[attack class]); } @end
下面就是管理攻击和人物的代码:
//创建人物 _avatar = [[Avatar alloc] init]; //穿金属甲 _metalArmored = [[MetalArmor alloc] init]; [_metalArmored setNextAttackHandler:_avatar]; //穿水晶盾 _crystalShield = [[CrystalShield alloc] init]; [_crystalShield setNextAttackHandler:_metalArmored]; _swordAttack = [[SwordAttack alloc] init]; _magicFireAttack = [[MagicFireAttack alloc] init]; _lightningAttack = [[LightningAttack alloc] init];
switch (attackType) { case SwordAttackType: { [_crystalShield handleAttack:_swordAttack]; _bloodCount -= 1; } break; case MagicFireAttackType: { [_crystalShield handleAttack:_magicFireAttack]; _bloodCount -= 2; } break; case LightningAttackType: { [_crystalShield handleAttack:_lightningAttack]; _bloodCount -= 5; } break; default: break; }
这个简单地RPG游戏的例子演示了如何使用责任链模式,来简化任务处理各种攻击的编码和逻辑。如果不用这个模式,防御逻辑很可能都塞到一个类中(如AVAtar中),代码会乱作一团。
总结
本例中将RPG游戏中的人物的各种防御机制时限为责任链模式。每种防御机制只能应付一种特定的攻击。一个攻击处理程序链决定了人物可以防御何种攻击。在游戏中,任何攻击处理程序都能在任何时间被添加或删除,而不会影响人物的其他行为。对于此类设计,责任链模式是很自然的选择。否则,攻击处理程序的复杂组合会让任务的代码量非常庞大,让处理程序变得非常困难。
Demo地址:https://github.com/haozheMa/DesignPatternsCollections