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