標籤:
狀態模式允許對象內部狀態改變時改變它的行為,對象看起來好像修改了它的類。狀態模式看起來和策略模式比較相像,策略模式是將可以互換的行為封裝起來,然後通過使用委託的方式,決定使用哪一個行為,狀態也是封裝行為,不同的是可以將行為委託到目前狀態。一個需要從外部設定,一個是內部通過狀態變更達到行為變成的目的。
基礎知識
狀態模式的UML類圖:
State封裝基本的狀態行為,我們通過Cotext上下文持有狀態子類的執行個體,外部發起請求,我們就可以委託狀態進行處理。地鐵裡面一般都有自動飲料售賣機,我們將所有的飲料都當做商品來看,我們如果需要買飲料需要兩步,投幣,取貨,如果沒有商品還可以退錢三種行為,關於自動售賣機的存在無錢狀態,有錢狀態,售賣中狀態,已售罄狀態四種狀態,稍微思考一下,可以看下面的具體實現。
設計實戰
狀態行為基類:
@protocol StateProtocol <NSObject>@optional-(void)putMoney;@optional-(void)ejectMoney;@optional-(void)pressButton;@optional-(void)distribute;@end@interface State : NSObject<StateProtocol>@end
售賣機處於無錢狀態(NoMoneyState):
@protocol NoMoneyDelegate <NSObject>-(void)setCurrentState:(State *)currentState;-(State *)geHasMoneyState;@end//沒錢狀態@interface NoMoneyState : State@property (weak,nonatomic) id<NoMoneyDelegate> delegate;@end
@implementation NoMoneyState-(void)putMoney{ NSLog(@"NoMoneyState-putMoney:投放錢幣"); [self.delegate setCurrentState:[self.delegate geHasMoneyState]];}-(void)ejectMoney{ NSLog(@"NoMoneyState-ejectMoney:沒有投入錢幣,無法退錢");}-(void)pressButton{ NSLog(@"NoMoneyState-pressButton:請先投幣");}-(void)distribute{ NSLog(@"NoMoneyState-pressButton:請投幣");}@end
售賣機處於有錢狀態(HasMoneyState):
@protocol HasMoneyDelegate <NSObject>-(void)setCurrentState:(State *)currentState;-(State *)getNoMoneyState;-(State *)getSoldState;@end//有錢狀態@interface HasMoneyState : State@property (weak,nonatomic) id<HasMoneyDelegate> delegate;@end
@implementation HasMoneyState-(void)putMoney{ NSLog(@"HasMoneyState-putMoney:已經投入了錢,暫不支援投入");}-(void)ejectMoney{ NSLog(@"HasMoneyState-ejectMoney:退錢,重新設定售賣機為無前狀態"); [self.delegate setCurrentState:[self.delegate getNoMoneyState]];}-(void)pressButton{ NSLog(@"HasMoneyState-pressButton:按鈕按下,取貨"); [self.delegate setCurrentState:[self.delegate getSoldState]];}-(void)distribute{ NSLog(@"HasMoneyState-distribute:無法進行取出商品");}@end
售賣機處於售賣狀態:
@protocol SoldDelegate <NSObject>-(void)setCurrentState:(State *)currentState;-(void)realseProduct;-(NSInteger)getCurrentCount;-(State *)getNoMoneyState;-(State *)getSoldOutState;@end//售出狀態@interface SoldState : State@property (weak,nonatomic) id<SoldDelegate> delegate;@end
@implementation SoldState-(void)putMoney{ NSLog(@"SoldState-putMoney:請稍後,進行中商品出售");}-(void)ejectMoney{ NSLog(@"SoldState-putMoney:請稍後,進行中商品出售,無法退錢");}-(void)pressButton{ NSLog(@"SoldState-putMoney:請在取出物品之後重新投幣");}-(void)distribute{ [self.delegate realseProduct]; if ([self.delegate getCurrentCount]) { [self.delegate setCurrentState:[self.delegate getNoMoneyState]]; }else{ [self.delegate setCurrentState:[self.delegate getSoldOutState]]; }}@end
售罄狀態(SoldOutState):
@protocol SoldOutDelegate <NSObject>-(void)setCurrentState:(State *)currentState;@end//售罄狀態@interface SoldOutState : State@property (weak,nonatomic) id<SoldOutDelegate> delegate;@end
@implementation SoldOutState-(void)putMoney{ NSLog(@"SoldOutState-PutMoney:已售罄");}-(void)ejectMoney{ NSLog(@"SoldOutState-ejectMoney:無法退錢");}-(void)pressButton{ NSLog(@"SoldOutState-pressButton:無法售出");}-(void)distribute{ NSLog(@"SoldOutState-distribute:無法分發");}@end
售賣機(SaleMachine):
@interface SaleMachine : NSObject<NoMoneyDelegate,HasMoneyDelegate,SoldOutDelegate,SoldDelegate>@property (strong,nonatomic) NoMoneyState *noMoneyState;@property (strong,nonatomic) HasMoneyState *hasMoneyState;@property (strong,nonatomic) SoldOutState *soldOutState;@property (strong,nonatomic) SoldState *soldState;-(instancetype)initWithCount:(NSInteger)count;-(void)setCurrentState:(State *)currentState;-(void)putMoney;-(void)ejectMoney;-(void)pressButton;@end
@interface SaleMachine()@property (strong,nonatomic) State *state;@property (assign,nonatomic) NSInteger productCount;@end@implementation SaleMachine-(instancetype)initWithCount:(NSInteger)count{ self=[super init]; if (self) { self.noMoneyState=[[NoMoneyState alloc]init]; self.noMoneyState.delegate=self; self.hasMoneyState=[[HasMoneyState alloc]init]; self.hasMoneyState.delegate=self; self.soldState=[[SoldState alloc]init]; self.soldState.delegate=self; self.soldOutState=[[SoldOutState alloc]init]; self.soldOutState.delegate=self; self.productCount=count; if (count) { self.state=self.noMoneyState; } } return self;}-(void)putMoney{ [self.state putMoney];}-(void)ejectMoney{ [self.state ejectMoney];}-(void)pressButton{ [self.state pressButton]; [self.state distribute];}-(void)setCurrentState:(State *)currentState{ self.state=currentState;}#pragma mark - NoMoenyDelegate-(State *)geHasMoneyState{ return self.hasMoneyState;}#pragma mark - HasMoneyDelegate-(State *)getNoMoneyState{ return self.noMoneyState;}-(State *)getSoldState{ return self.soldState;}#pragma mark - SoldDelegate-(void)realseProduct{ NSLog(@"SoldDelegate-realseProduct:商品售出"); if (self.productCount) { self.productCount-=1; }}-(State *)getSoldOutState{ return self.soldOutState;}-(NSInteger)getCurrentCount{ return self.productCount;}@end
測試:
SaleMachine *machine=[[SaleMachine alloc]initWithCount:1]; [machine putMoney]; [machine ejectMoney]; [machine putMoney]; [machine pressButton]; SaleMachine *next=[[SaleMachine alloc]initWithCount:1]; [next putMoney]; [next ejectMoney];
測試效果:
狀態模式的優缺點:
優點:狀態模式允許一個對象基於內部狀態有不同的行為,將行為委託給狀態物件執行,狀態轉化可以由Context也可以由狀態行為控制,比較靈活;
缺點: 狀態模式的使用必然會增加系統類別和對象的個數。狀態模式的結構與實現都較為複雜,如果使用不當將導致程式結構和代碼的混亂。
iOS開發-狀態模式