iOS設計模式--責任鏈模式

jopen 9年前發布 | 28K 次閱讀 IOS iOS開發 移動開發

本篇文章參照Objective-C編程之道,iOS設計模式解析一書(Carlo Chung)而來的,參考了其大量的實例與思想,通過寫博客的方式,讓自己對這些iOS中常見的設計模式做一個總結,以加深自己對設計模式的理解與應用。希望能夠對大家有點幫助。摘要 本篇文章參照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父類的代碼,代碼如下:

#import <Foundation/Foundation.h>

import "Attack.h"

@interface AttackHandler : NSObject

@property (nonatomic, strong) AttackHandler *nextAttackHandler;

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

@end</pre>

    AttackHandler定義了一個同類型的私有變量nextAttackHandler,它是攻擊的下一個響應者。AttackHander的子類應該重載handleAttack:方法,以響應它能夠識別的一種攻擊。抽象的AttackHandler為這個方法定義了默認行為,代碼如下:

#import "AttackHandler.h"

@implementation AttackHandler

- (void)handleAttack:(Attack *)attack {     // 默認調用nextAttackHandler進行處理。     [_nextAttackHandler handleAttack:attack]; }

@end</pre>

    如果子類沒有重載這個方法,默認的handleAttack:實現就會被調用。這個方法只是把攻擊傳給nextAttackHandler去處理。

    接下來看下任務的第一個防御道具WaterAttackHandler,WaterAttackHandler子類化AttackHandler并重載其handleAttack:方法,代碼如下:

#import "AttackHandler.h"

@interface WaterAttackHandler : AttackHandler

// 重寫處理攻擊方法 - (void)handleAttack:(Attack *)attack;

@end</pre>

    在.h中再次聲明重載的方法不是必需的,但是這樣做更加清晰。WaterAttackHandler只能識別WaterAttack的實例,如果攻擊確實是WaterAttack類型,那么handleAttack:將用NSLog輸出@"我擋下了水的攻擊";否則,它輸出另一條消息并使用[super handleAttack:attack];把攻擊轉給super,代碼如下:

#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</pre>

    類似的FireAttackHandler也是同樣的道理,代碼如下:

#import "AttackHandler.h"

@interface FireAttackHandler : AttackHandler

// 重寫處理攻擊的方法 - (void)handleAttack:(Attack *)attack;

@end</pre>

#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</pre>

    如果沒有防具能夠對付攻擊,攻擊最終將傳給Avatar(游戲人物)。Avatar也是AttackHandler的子類,而且與WaterAttackHandler、FireAttackHandler有相同的響應機制。但是,攻擊到這里的時候,Avatar將沒有防御而受到損傷。將會輸出NSLog(@"我被%@攻擊到了", [attack class]);,輸出攻擊的名稱。代碼如下:

#import "AttackHandler.h"

@interface Avatar : AttackHandler

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

@end</pre>

#import "Avatar.h"

@implementation Avatar

- (void)handleAttack:(Attack *)attack {     NSLog(@"我被%@攻擊到了", [attack class]); }

@end</pre>

    現在已經定義好了所有的AttackHandler。來看下客戶端代碼,看看下責任鏈模式的使用,客戶端代碼如下:

#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</pre>

    這個攻擊有點像棧(先進后出)。因為需要讓Avatar是攻擊的最后一站,所以它要最先創建。然后創WaterAttackHandler的實例,把Avatar作為它的下一個AttackHandler。它們被當做增強了Avatar,WaterAttackHandler是它通往真正Avatar實例的第一道門。然后,添加FireAttackHandler作為Avatar的另一種防御。此時,Avatar已經具有兩種防御道具了。

    在游戲中的某個時刻,我們創建了3中類型的攻擊——waterAttack、fireAttack、soliderAttack,調用handleAttack:方法去處理,以下是來自責任鏈中各種AttackHandler的輸出,客戶端輸出如下:

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

</div>

來自:http://my.oschina.net/daguoshi/blog/495573

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!