參考部落格iOS App的設計模式開發中對State狀態模式的運用
使用場景:
例子1:按鈕來控制一個電梯的狀態,一個電梯開們,關門,停,執行。每一種狀態改變,都有可能要根據其他狀態來更新處理。例如,開門狀體,你不能在執行的時候開門,而是在電梯定下後才能開門。
例子2:我們給一部手機打電話,就可能出現這幾種情況:使用者開機,使用者關機,使用者欠費停機,使用者消戶等。 所以當我們撥打這個號碼的時候:系統就要判斷,該使用者是否在開機且不忙狀態,又或者是關機,欠費等狀態。但不管是那種狀態我們都應給出對應的處理操作。
基本概念
狀態模式(State),當一個物件的內下狀態改變時,允許改變其行為,這個物件看起來像是改變了其類。
狀態模式主要解決的是當控制一個物件狀態轉換的條件表示式過於複雜時的情況。把狀態的判斷邏輯轉移到表示不同狀態的一系列類當中,可以把複雜的判斷邏輯簡化。
環境類(Context): 定義客戶感興趣的介面。維護一個ConcreteState子類的例項,這個例項定義當前狀態。
抽象狀態類(State): 定義一個介面以封裝與Context的一個特定狀態相關的行為。
具體狀態類(ConcreteState): 每一子類實現一個與Context的一個狀態相關的行為。
工作狀態-狀態模式版:
程式碼實現:
//抽象狀態
#import "Work.h"
@interface State : NSObject
- (void)writeProgramWithWork:(Work *)work;// 工作
@end
// 上午工作狀態
#import "ForenoonState.h"
#import "NoonState.h"
@implementation ForenoonState
- (void)writeProgramWithWork:(Work *)work
{
if (work.hour< 12) {
NSLog(@"當前時間為%zd 上午工作 精神百倍",work.hour);
}else{
NoonState *state = [[NoonState alloc] init];
[state writeProgramWithWork:work];
}
}
//中午狀態
#import "NoonState.h"
#import "AfternoonState.h"
@implementation NoonState
- (void)writeProgramWithWork:(Work *)work
{
if (work.hour< 13) {
NSLog(@"當前時間為%zd 餓了 困了 吃飯 午休",work.hour);
}else{
AfternoonState *state = [[AfternoonState alloc] init];
[state writeProgramWithWork:work];
}
}
// 下午狀態
#import "AfternoonState.h"
#import "EveningState.h"
@implementation AfternoonState
- (void)writeProgramWithWork:(Work *)work
{
if (work.hour< 17) {
NSLog(@"當前時間為%zd 下午狀態還不錯,繼續努力",work.hour);
}else{
EveningState *state = [[EveningState alloc] init];
[state writeProgramWithWork:work];
}
}
@end
// 晚上狀態
#import "EveningState.h"
#import "SleepingState.h"
#import "RestState.h"
@implementation EveningState
- (void)writeProgramWithWork:(Work *)work
{
if (work.finish) {// 如果任務完成就休息
RestState *state = [[RestState alloc] init];
[state writeProgramWithWork:work];
}else{
if (work.hour< 21) {
NSLog(@"當前時間為%zd 生活不易需要加班",work.hour);
}else{
SleepingState *state = [[SleepingState alloc] init];
[state writeProgramWithWork:work];
}
}
}
@end
// 睡覺狀態
#import "SleepingState.h"
@implementation SleepingState
- (void)writeProgramWithWork:(Work *)work
{
NSLog(@"實在困的不行了,要好好的休息");
}
@end
// 下班休息狀態
#import "RestState.h"
@implementation RestState
- (void)writeProgramWithWork:(Work *)work
{
NSLog(@"下班回家好好休息");
}
@end
// work類的實現
@class State;
@interface Work : NSObject
@property (nonatomic, strong)State *current;
@property (nonatomic, assign)int hour;
@property (nonatomic, assign)BOOL finish;
- (void)writeProgram;
@end
#import "Work.h"
#import "ForenoonState.h"
@implementation Work
- (instancetype)init
{
self = [super init];
if (self) {
self.current = [[ForenoonState alloc]init];
}
return self;
}
- (void)writeProgram
{
[self.current writeProgramWithWork:self];
}
@end
通過這個例子,可以看到,狀態模式通過把各種狀態轉移邏輯分佈到State的子類之間,來減少相互間的依賴。當一個物件的行為取決於它的狀態,並且它必須在執行時刻根據狀態改變它的行為時,就可以考慮使用狀態模式了。
State模式有下面一些效果:
狀態模式的優點:
1 ) 它將與特定狀態相關的行為區域性化,並且將不同狀態的行為分割開來: State模式將所有與一個特定的狀態相關的行為都放入一個物件中。因為所有與狀態相關的程式碼都存在於某一個State子類中, 所以通過定義新的子類可以很容易的增加新的狀態和轉換。另一個方法是使用資料值定義內部狀態並且讓 Context操作來顯式地檢查這些資料。但這樣將會使整個Context的實現中遍佈看起來很相似的條件if else語句或switch case語句。增加一個新的狀態可能需要改變若干個操作, 這就使得維護變得複雜了。State模式避免了這個問題, 但可能會引入另一個問題, 因為該模式將不同狀態的行為分佈在多個State子類中。這就增加了子類的數目,相對於單個類的實現來說不夠緊湊。但是如果有許多狀態時這樣的分佈實際上更好一些, 否則需要使用巨大的條件語句。正如很長的過程一樣,巨大的條件語句是不受歡迎的。它們形成一大整塊並且使得程式碼不夠清晰,這又使得它們難以修改和擴充套件。 State模式提供了一個更好的方法來組織與特定狀態相關的程式碼。決定狀態轉移的邏輯不在單塊的 i f或s w i t c h語句中, 而是分佈在State子類之間。將每一個狀態轉換和動作封裝到一個類中,就把著眼點從執行狀態提高到整個物件的狀態。這將使程式碼結構化並使其意圖更加清晰。
2) 它使得狀態轉換顯式化: 當一個物件僅以內部資料值來定義當前狀態時 , 其狀態僅表現為對一些變數的賦值,這不夠明確。為不同的狀態引入獨立的物件使得轉換變得更加明確。而且, State物件可保證Context不會發生內部狀態不一致的情況,因為從 Context的角度看,狀態轉換是原子的—只需重新繫結一個變數(即Context的State物件變數),而無需為多個變數賦值
3) State物件可被共享 如果State物件沒有例項變數—即它們表示的狀態完全以它們的型別來編碼—那麼各Context物件可以共享一個State物件。當狀態以這種方式被共享時, 它們必然是沒有內部狀態, 只有行為的輕量級物件。
狀態模式的缺點:
1) 狀態模式的使用必然會增加系統類和物件的個數。
2) 狀態模式的結構與實現都較為複雜,如果使用不當將導致程式結構和程式碼的混亂。
具體實踐聯想:
在公司中有一個流程是課程的報名那麼他會有多種狀態,例如結束,開始,報名成功,報名成功和支付成功等,針對這些不同的狀態具體的操作又不一樣,同時還有包含的關係,例如開始報名的話,就需要報名,和支付兩個狀態同時出現,這個剛好進入的狀態是開始報名,當報名成功後將狀態變為報名成功,在呼叫報名成功狀態下的方法,這樣既可以將各個操作簡單的寫一次,也可以將各個操作分開處理,利於程式碼的梳理。