责任链模式

Objective-C编程之道 iOS设计模式解析
iOS设计模式解析-工厂模式
iOS设计模式解析-抽象工厂模式
iOS设计模式解析-外观模式
iOS设计模式解析-中介者模式
iOS设计模式解析-观察者模式
iOS设计模式解析-装饰模式
iOS设计模式解析-责任链模式
iOS设计模式解析-模板方法
iOS设计模式解析-策略模式
iOS设计模式解析-享元模式
iOS设计模式解析-代码地址

何为责任链模式

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

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

何时使用责任链模式

在以下情形,自然会考虑使用这一模式。

  • 有多个对象可以处理请求,而处理程序只有在运行时才能确定;
  • 向一组对象发出请求,而不想显式指定处理请求的特定处理程序。

在RPG游戏中使用责任链模式

责任链模式_第1张图片
image.png

假设要实现两种防御:金属盔甲和水晶盾牌,它们都只能按照设计对付某些攻击,金属盔甲可以防御剑的攻击,而水晶盾牌可以对付任何魔法火焰的攻击,人物也是响应链的一部分,因此它也应该跟其他防御道具具有共同的行为,对攻击做出响应。

结构

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

AttackHandler
#import 
#import "Attack.h"

@interface AttackHandler : NSObject

@property (nonatomic, strong) AttackHandler *nextAttackHandler;

- (void) handleAttack:(Attack *)attack;

@end
#import "AttackHandler.h"

@implementation AttackHandler

- (void) handleAttack:(Attack *)attack
{
  [self.nextAttackHandler handleAttack:attack];
}

@end

AttackHandler定义了了一个同类型的变量nextAttackHandler,它是攻击的下一个响应者,AttackHandler的子类应该重载handleAttack:方法,以响应它能够识别的一种攻击。如果子类没有重载这个方法,默认的handleAttack:实现就会被调用。这个方法只是把攻击传给nextAttackHandler去处理。

MetalArmor
#import 
#import "AttackHandler.h"

@interface MetalArmor : AttackHandler

// overridden method
- (void) handleAttack:(Attack *)attack;

@end
#import "MetalArmor.h"
#import "SwordAttack.h"

@implementation MetalArmor

- (void) handleAttack:(Attack *)attack
{
  if ([attack isKindOfClass:[SwordAttack class]])
  {
    // no damage beyond this armor
    NSLog(@"%@", @"No damage from a sword attack!");
  }
  else 
  {
    NSLog(@"%@ I don't know this attack: %@",[self class],[attack class]);
    [super handleAttack:attack];
  }
}
@end
CrystalShield
#import 
#import "AttackHandler.h"

@interface CrystalShield : AttackHandler

// overridden method
- (void) handleAttack:(Attack *)attack;

@end
#import "CrystalShield.h"
#import "MagicFireAttack.h"

@implementation CrystalShield

- (void) handleAttack:(Attack *)attack
{
  if ([attack isKindOfClass:[MagicFireAttack class]])
  {
    // no damage beyond this shield
    NSLog(@"%@", @"No damage from a magic fire attack!");
  }
  else 
  {
    NSLog(@"%@ I don't know this attack: %@", [self class],[attack class]);
    [super handleAttack:attack];
  }
}

@end

如果没有防具能够对付攻击,攻击最终将传给Avatar。Avatar也是AttackHandler的子类,而且与MetalArmor和CrystalShield有相同的响应Attack的机制。但是,攻击到达这里的时候Avatar将没有防御而受到损伤。

Avatar
#import "Avatar.h"


@implementation Avatar

- (void) handleAttack:(Attack *)attack
{
  // when an attack reaches this point,
  // I'm hit.
  // actual points taken off depends on
  // the type of attack.
  NSLog(@"Oh! I'm hit with a %@!", [attack class]);
}

@end
调用
- (void)viewDidLoad {
    [super viewDidLoad];
    // create a new avatar
    AttackHandler *avatar = [[Avatar alloc] init];
    
    // put it in metal armor
    AttackHandler *metalArmoredAvatar = [[MetalArmor alloc] init];
    [metalArmoredAvatar setNextAttackHandler:avatar];
    
    // then add a crytal shield
    // to the avatar who's in
    // a metal armor
    AttackHandler *superAvatar = [[CrystalShield alloc] init];
    [superAvatar setNextAttackHandler:metalArmoredAvatar];
    
    // ... some other actions
    
    // attack the avatar with
    // a sword
    Attack *swordAttack = [[SwordAttack alloc] init];
    [superAvatar handleAttack:swordAttack];
    
    // then attack the avatar with
    // magic fire
    Attack *magicFireAttack = [[MagicFireAttack alloc] init];
    [superAvatar handleAttack:magicFireAttack];
    
    // now there is a new attack
    // with lightning...
    Attack *lightningAttack = [[LightningAttack alloc] init];
    [superAvatar handleAttack:lightningAttack];
}

这个攻击处理程序有点像栈(即先进后出)。因为需要让Avatar是攻击的最后一站,所以它要最先创建。然后创建MetalArmor的实例,把Avatar作为它的下一个AttackHandler它们被当做“增强’了的Avatar, MetalArmor 是它通往真正Avatar实例的第一道门。仅有MetalArmor还不够,还需要创建CrystalShiela的实例,作为Avatar的另一种防御。我们使用MetalArmor形式的AttackHandler ( 与Avatar连接在一起),作为CrystalShield实例的下一个攻击处理程序。此时,Avatar已是一个具有两种防御的‘ 超级人物”。在游戏中的某个时刻,我们创建了3种类型的攻击:swordAttack、 magicFireAttack和lightningAttack,让“超级人物”用它的handleAttack:方法去处理。以下是来自责任链中各种AttackHandler的输出,通过这些输出可以了解发生了什么。

输出
image.png

金属盔甲为人物挡住了剑的攻击,因为有水晶盾牌,魔法火焰攻击也没有伤到人物。但是第三次的闪电攻击,盔甲和盾牌都不知道如何应付,而是打出了消息:I don't know this attack:LightningAttack (我不认识这个攻击:闪电攻击)。最后,攻击由人物自己来处理,打出了消息oh! I'm hit with a LightningAttack! (啊!我被闪电攻击击中了!),表示因闪电攻击而受到损伤。

总结

把RPG游戏中人物的各种防御机制实现为责任链模式。每种防御机制只能应付一种特定的攻击。一个攻击处理程序链决定了人物可以防御何种攻击。在游戏过程中,任何攻击处理程序都能在任何时间被添加或删除,而不会影响人物的其他行为。对于此类设计,责任链模式是很自然的选择。否则,攻击处理程序的复杂组合会让人物的代码非常庞大,让处理程序的变更非常困难。

你可能感兴趣的:(责任链模式)