设计模式(责任链模式-行为扩展)

声明:本系列文章内容摘自《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