iOS 設(shè)計模式之二十一(狀態(tài)模式)

一、概念

1、狀態(tài)模式的動機

? 現(xiàn)在年輕人喜歡玩游戲,有些游戲需要充錢才能獲得更高的戰(zhàn)斗力,每充錢到一定程度,就會達到某種會員狀態(tài),在這種狀態(tài)玩家能有更好的游戲體驗。在軟件系統(tǒng)中,為了更好地對這些具有多種狀態(tài)的對象進行設(shè)計,我們可以使用一種被稱之為狀態(tài)模式的設(shè)計模式。

2、狀態(tài)模式的定義

? 狀態(tài)模式(State Pattern):允許一個對象在其內(nèi)部狀態(tài)改變時改變它的行為,對象看起來似乎修改了它的類。其別名為狀態(tài)對象(Objects for States),狀態(tài)模式是一種對象行為型模式。

? 狀態(tài)模式用于解決系統(tǒng)中復(fù)雜對象的狀態(tài)轉(zhuǎn)換以及不同狀態(tài)下行為的封裝問題。

3、狀態(tài)模式的3個角色

1)Context(環(huán)境類):環(huán)境類又稱為上下文類,它是擁有多種狀態(tài)的對象。由于環(huán)境類的狀態(tài)存在多樣性且在不同狀態(tài)下對象的行為有所不同,因此將狀態(tài)獨立出去形成單獨的狀態(tài)類。在環(huán)境類中維護一個抽象狀態(tài)類State的實例,這個實例定義當前狀態(tài),在具體實現(xiàn)時,它是一個State子類的對象。

2)State(抽象狀態(tài)類):它用于定義一個接口以封裝與環(huán)境類的一個特定狀態(tài)相關(guān)的行為,在抽象狀態(tài)類中聲明了各種不同狀態(tài)對應(yīng)的方法,而在其子類中實現(xiàn)類這些方法,由于不同狀態(tài)下對象的行為可能不同,因此在不同子類中方法的實現(xiàn)可能存在不同,相同的方法可以寫在抽象狀態(tài)類中。

3)ConcreteState(具體狀態(tài)類):它是抽象狀態(tài)類的子類,每一個子類實現(xiàn)一個與環(huán)境類的一個狀態(tài)相關(guān)的行為,每一個具體狀態(tài)類對應(yīng)環(huán)境的一個具體狀態(tài),不同的具體狀態(tài)類其行為有所不同。

? 在狀態(tài)模式中,我們將對象在不同狀態(tài)下的行為封裝到不同的狀態(tài)類中,為了讓系統(tǒng)具有更好的靈活性和可擴展性,同時對各狀態(tài)下的共有行為進行封裝,我們需要對狀態(tài)進行抽象,引入了抽象狀態(tài)類角色。

4、結(jié)構(gòu)圖
狀態(tài)模式

二、示例

? 在狀態(tài)模式中引入了抽象狀態(tài)類和具體狀態(tài)類,它們是狀態(tài)模式的核心。在狀態(tài)模式中實現(xiàn)狀態(tài)轉(zhuǎn)換時,具體狀態(tài)類可通過調(diào)用環(huán)境類Context的setState()方法進行狀態(tài)的轉(zhuǎn)換操作,也可以統(tǒng)一由環(huán)境類Context來實現(xiàn)狀態(tài)的轉(zhuǎn)換。

1)先創(chuàng)建一個MemberState協(xié)議,有個handle()方法,表示抽象狀態(tài)類;

2)然后創(chuàng)建四個類NormalMemberState、GoldMemberState、PlatinumMemberState和DiamondMemberState,都遵循MemberState協(xié)議,表示具體狀態(tài)類;

3)最后創(chuàng)建一個Person類,有一個state屬性,表示環(huán)境類。

具體代碼如下:

MemberState協(xié)議:

// 會員狀態(tài)
@protocol MemberState <NSObject>
- (void)handle; //聲明抽象業(yè)務(wù)方法,不同的具體狀態(tài)類可以不同的實現(xiàn)
@end

typedef id<MemberState> MemberState;

NormalMemberState、GoldMemberState、PlatinumMemberState和DiamondMemberState類:

// NormalMemberState 普通會員
@interface NormalMemberState : NSObject<MemberState>
@end
@implementation NormalMemberState
- (void)handle {
    NSLog(@"普通會員:剛出生,攻擊力5,只能打敗普通人");
}
@end

// GoldMemberState 黃金會員
@interface GoldMemberState : NSObject<MemberState>
@end
@implementation GoldMemberState
- (void)handle {
    NSLog(@"黃金會員:長大了,攻擊力200,打敗比克大魔王");
}
@end

// PlatinumMemberState 白金會員
@interface PlatinumMemberState : NSObject<MemberState>
@end
@implementation PlatinumMemberState
- (void)handle {
    NSLog(@"白金會員:變身超級賽亞人1,攻擊力20000,擊敗宇宙魔王弗利薩");
}
@end

// DiamondMemberState 鉆石會員
@interface DiamondMemberState : NSObject<MemberState>
@end
@implementation DiamondMemberState
- (void)handle {
    NSLog(@"鉆石會員:變身超級賽亞人2,攻擊力200000,擊殺生化人沙魯");
}
@end

Person類:

// 環(huán)境類
@interface Person : NSObject
// 其他屬性值,該屬性值的變化可能會導(dǎo)致對象狀態(tài)發(fā)生變化
@property(nonatomic, assign) NSInteger money;
@property(nonatomic, strong) MemberState state; //維持一個對抽象狀態(tài)對象的引用
- (void)playGame;
@end

@implementation Person
- (void)setMoney:(NSInteger)money {
    _money = money;
    [self changeState];
}

// 判斷屬性值,根據(jù)屬性值進行狀態(tài)轉(zhuǎn)換
- (void)changeState {
    NSInteger money = self.money;
    if (money <= 0) {
        NSLog(@"請充值,才能有更好的游戲體驗");
        return;
    }
    
    if (money < 10) {
        self.state = [NormalMemberState new];
    } else if (money < 100) {
        self.state = [GoldMemberState new];
    } else if (money < 1000) {
        self.state = [PlatinumMemberState new];
    } else {
        self.state = [DiamondMemberState new];
    }
}

- (void)playGame {
    [self.state handle]; //調(diào)用狀態(tài)對象的業(yè)務(wù)方法
}
@end

運行代碼:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 創(chuàng)建孫悟空對象
    Person *monkeyKing = [Person new];
    monkeyKing.money = 5;
    [monkeyKing playGame];
    
    monkeyKing.money = 50;
    [monkeyKing playGame];
    
    monkeyKing.money = 500;
    [monkeyKing playGame];
    
    monkeyKing.money = 5000;
    [monkeyKing playGame];
}

打印結(jié)果:

普通會員:剛出生,攻擊力5,只能打敗普通人
黃金會員:長大了,攻擊力200,打敗比克大魔王
白金會員:變身超級賽亞人1,攻擊力20000,擊敗宇宙魔王弗利薩
鉆石會員:變身超級賽亞人2,攻擊力200000,擊殺生化人沙魯

三、總結(jié)

? 狀態(tài)模式將一個對象在不同狀態(tài)下的不同行為封裝在一個個狀態(tài)類中,通過設(shè)置不同的狀態(tài)對象可以讓環(huán)境對象擁有不同的行為,而狀態(tài)轉(zhuǎn)換的細節(jié)對于客戶端而言是透明的,方便了客戶端的使用。

1、優(yōu)點

1、 封裝了狀態(tài)的轉(zhuǎn)換規(guī)則,在狀態(tài)模式中可以將狀態(tài)的轉(zhuǎn)換代碼封裝在環(huán)境類或者具體狀態(tài)類中,可以對狀態(tài)轉(zhuǎn)換代碼進行集中管理,而不是分散在一個個業(yè)務(wù)方法中。

2、將所有與某個狀態(tài)有關(guān)的行為放到一個類中,只需要注入一個不同的狀態(tài)對象即可使環(huán)境對象擁有不同的行為。

3、允許狀態(tài)轉(zhuǎn)換邏輯與狀態(tài)對象合成一體,而不是提供一個巨大的條件語句塊,狀態(tài)模式可以讓我們避免使用龐大的條件語句來將業(yè)務(wù)方法和狀態(tài)轉(zhuǎn)換代碼交織在一起。

4、可以讓多個環(huán)境對象共享一個狀態(tài)對象,從而減少系統(tǒng)中對象的個數(shù)。

2、缺點

1、狀態(tài)模式的使用必然會增加系統(tǒng)中類和對象的個數(shù),導(dǎo)致系統(tǒng)運行開銷增大。

2、狀態(tài)模式的結(jié)構(gòu)與實現(xiàn)都較為復(fù)雜,如果使用不當將導(dǎo)致程序結(jié)構(gòu)和代碼的混亂,增加系統(tǒng)設(shè)計的難度。

3、狀態(tài)模式對“開閉原則”的支持并不太好,增加新的狀態(tài)類需要修改那些負責狀態(tài)轉(zhuǎn)換的源代碼,否則無法轉(zhuǎn)換到新增狀態(tài)。

3、適用場景

1、對象的行為依賴于它的狀態(tài)(如某些屬性值),狀態(tài)的改變將導(dǎo)致行為的變化。

2、在代碼中包含大量與對象狀態(tài)有關(guān)的條件語句,這些條件語句的出現(xiàn),會導(dǎo)致代碼的可維護性和靈活性變差,不能方便地增加和刪除狀態(tài),并且導(dǎo)致客戶類與類庫之間的耦合增強。

Demo地址:iOS-Design-Patterns

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容