參考博客iOS App的設(shè)計(jì)模式開發(fā)中對(duì)State狀態(tài)模式的運(yùn)用
使用場(chǎng)景:
例子1:按鈕來(lái)控制一個(gè)電梯的狀態(tài)想幻,一個(gè)電梯開們恋谭,關(guān)門,停漓踢,運(yùn)行牵署。每一種狀態(tài)改變,都有可能要根據(jù)其他狀態(tài)來(lái)更新處理喧半。例如奴迅,開門狀體,你不能在運(yùn)行的時(shí)候開門挺据,而是在電梯定下后才能開門取具。
例子2:我們給一部手機(jī)打電話脖隶,就可能出現(xiàn)這幾種情況:用戶開機(jī),用戶關(guān)機(jī)者填,用戶欠費(fèi)停機(jī)浩村,用戶消戶等做葵。 所以當(dāng)我們撥打這個(gè)號(hào)碼的時(shí)候:系統(tǒng)就要判斷占哟,該用戶是否在開機(jī)且不忙狀態(tài),又或者是關(guān)機(jī)酿矢,欠費(fèi)等狀態(tài)榨乎。但不管是那種狀態(tài)我們都應(yīng)給出對(duì)應(yīng)的處理操作。
基本概念
狀態(tài)模式(State)瘫筐,當(dāng)一個(gè)對(duì)象的內(nèi)下狀態(tài)改變時(shí)蜜暑,允許改變其行為,這個(gè)對(duì)象看起來(lái)像是改變了其類策肝。
狀態(tài)模式主要解決的是當(dāng)控制一個(gè)對(duì)象狀態(tài)轉(zhuǎn)換的條件表達(dá)式過(guò)于復(fù)雜時(shí)的情況肛捍。把狀態(tài)的判斷邏輯轉(zhuǎn)移到表示不同狀態(tài)的一系列類當(dāng)中,可以把復(fù)雜的判斷邏輯簡(jiǎn)化之众。
環(huán)境類(Context):? 定義客戶感興趣的接口拙毫。維護(hù)一個(gè)ConcreteState子類的實(shí)例,這個(gè)實(shí)例定義當(dāng)前狀態(tài)棺禾。
抽象狀態(tài)類(State):? 定義一個(gè)接口以封裝與Context的一個(gè)特定狀態(tài)相關(guān)的行為缀蹄。
具體狀態(tài)類(ConcreteState):? 每一子類實(shí)現(xiàn)一個(gè)與Context的一個(gè)狀態(tài)相關(guān)的行為。
工作狀態(tài)-狀態(tài)模式版:
代碼實(shí)現(xiàn):
//抽象狀態(tài)
#import "Work.h"
@interface State : NSObject
- (void)writeProgramWithWork:(Work *)work;// 工作
@end
// 上午工作狀態(tài)
#import "ForenoonState.h"
#import "NoonState.h"
@implementation ForenoonState
- (void)writeProgramWithWork:(Work *)work
{
if (work.hour< 12) {
NSLog(@"當(dāng)前時(shí)間為%zd 上午工作 精神百倍",work.hour);
}else{
NoonState *state = [[NoonState alloc] init];
[state writeProgramWithWork:work];
}
}
//中午狀態(tài)
#import "NoonState.h"
#import "AfternoonState.h"
@implementation NoonState
- (void)writeProgramWithWork:(Work *)work
{
if (work.hour< 13) {
NSLog(@"當(dāng)前時(shí)間為%zd 餓了 困了 吃飯 午休",work.hour);
}else{
AfternoonState *state = [[AfternoonState alloc] init];
[state writeProgramWithWork:work];
}
}
// 下午狀態(tài)
#import "AfternoonState.h"
#import "EveningState.h"
@implementation AfternoonState
- (void)writeProgramWithWork:(Work *)work
{
if (work.hour< 17) {
NSLog(@"當(dāng)前時(shí)間為%zd 下午狀態(tài)還不錯(cuò)膘婶,繼續(xù)努力",work.hour);
}else{
EveningState *state = [[EveningState alloc] init];
[state writeProgramWithWork:work];
}
}
@end
// 晚上狀態(tài)
#import "EveningState.h"
#import "SleepingState.h"
#import "RestState.h"
@implementation EveningState
- (void)writeProgramWithWork:(Work *)work
{
if (work.finish) {// 如果任務(wù)完成就休息
RestState *state = [[RestState alloc] init];
[state writeProgramWithWork:work];
}else{
if (work.hour< 21) {
NSLog(@"當(dāng)前時(shí)間為%zd 生活不易需要加班",work.hour);
}else{
SleepingState *state = [[SleepingState alloc] init];
[state writeProgramWithWork:work];
}
}
}
@end
// 睡覺狀態(tài)
#import "SleepingState.h"
@implementation SleepingState
- (void)writeProgramWithWork:(Work *)work
{
NSLog(@"實(shí)在困的不行了缺前,要好好的休息");
}
@end
// 下班休息狀態(tài)
#import "RestState.h"
@implementation RestState
- (void)writeProgramWithWork:(Work *)work
{
NSLog(@"下班回家好好休息");
}
@end
// work類的實(shí)現(xiàn)
@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
通過(guò)這個(gè)例子,可以看到悬襟,狀態(tài)模式通過(guò)把各種狀態(tài)轉(zhuǎn)移邏輯分布到State的子類之間衅码,來(lái)減少相互間的依賴。當(dāng)一個(gè)對(duì)象的行為取決于它的狀態(tài)脊岳,并且它必須在運(yùn)行時(shí)刻根據(jù)狀態(tài)改變它的行為時(shí)肆良,就可以考慮使用狀態(tài)模式了。
State模式有下面一些效果:
狀態(tài)模式的優(yōu)點(diǎn):
1 ) 它將與特定狀態(tài)相關(guān)的行為局部化逸绎,并且將不同狀態(tài)的行為分割開來(lái): State模式將所有與一個(gè)特定的狀態(tài)相關(guān)的行為都放入一個(gè)對(duì)象中惹恃。因?yàn)樗信c狀態(tài)相關(guān)的代碼都存在于某一個(gè)State子類中, 所以通過(guò)定義新的子類可以很容易的增加新的狀態(tài)和轉(zhuǎn)換。另一個(gè)方法是使用數(shù)據(jù)值定義內(nèi)部狀態(tài)并且讓 Context操作來(lái)顯式地檢查這些數(shù)據(jù)棺牧。但這樣將會(huì)使整個(gè)Context的實(shí)現(xiàn)中遍布看起來(lái)很相似的條件if else語(yǔ)句或switch case語(yǔ)句巫糙。增加一個(gè)新的狀態(tài)可能需要改變?nèi)舾蓚€(gè)操作, 這就使得維護(hù)變得復(fù)雜了。State模式避免了這個(gè)問(wèn)題, 但可能會(huì)引入另一個(gè)問(wèn)題, 因?yàn)樵撃J綄⒉煌瑺顟B(tài)的行為分布在多個(gè)State子類中颊乘。這就增加了子類的數(shù)目参淹,相對(duì)于單個(gè)類的實(shí)現(xiàn)來(lái)說(shuō)不夠緊湊醉锄。但是如果有許多狀態(tài)時(shí)這樣的分布實(shí)際上更好一些, 否則需要使用巨大的條件語(yǔ)句。正如很長(zhǎng)的過(guò)程一樣浙值,巨大的條件語(yǔ)句是不受歡迎的恳不。它們形成一大整塊并且使得代碼不夠清晰,這又使得它們難以修改和擴(kuò)展开呐。 State模式提供了一個(gè)更好的方法來(lái)組織與特定狀態(tài)相關(guān)的代碼烟勋。決定狀態(tài)轉(zhuǎn)移的邏輯不在單塊的 i f或s w i t c h語(yǔ)句中, 而是分布在State子類之間。將每一個(gè)狀態(tài)轉(zhuǎn)換和動(dòng)作封裝到一個(gè)類中筐付,就把著眼點(diǎn)從執(zhí)行狀態(tài)提高到整個(gè)對(duì)象的狀態(tài)卵惦。這將使代碼結(jié)構(gòu)化并使其意圖更加清晰。
2) 它使得狀態(tài)轉(zhuǎn)換顯式化: 當(dāng)一個(gè)對(duì)象僅以內(nèi)部數(shù)據(jù)值來(lái)定義當(dāng)前狀態(tài)時(shí) , 其狀態(tài)僅表現(xiàn)為對(duì)一些變量的賦值瓦戚,這不夠明確沮尿。為不同的狀態(tài)引入獨(dú)立的對(duì)象使得轉(zhuǎn)換變得更加明確。而且, State對(duì)象可保證Context不會(huì)發(fā)生內(nèi)部狀態(tài)不一致的情況较解,因?yàn)閺?Context的角度看畜疾,狀態(tài)轉(zhuǎn)換是原子的—只需重新綁定一個(gè)變量(即Context的State對(duì)象變量),而無(wú)需為多個(gè)變量賦值
3) State對(duì)象可被共享 如果State對(duì)象沒有實(shí)例變量—即它們表示的狀態(tài)完全以它們的類型來(lái)編碼—那么各Context對(duì)象可以共享一個(gè)State對(duì)象印衔。當(dāng)狀態(tài)以這種方式被共享時(shí), 它們必然是沒有內(nèi)部狀態(tài), 只有行為的輕量級(jí)對(duì)象啡捶。
狀態(tài)模式的缺點(diǎn):
1) 狀態(tài)模式的使用必然會(huì)增加系統(tǒng)類和對(duì)象的個(gè)數(shù)。
2) 狀態(tài)模式的結(jié)構(gòu)與實(shí)現(xiàn)都較為復(fù)雜当编,如果使用不當(dāng)將導(dǎo)致程序結(jié)構(gòu)和代碼的混亂届慈。
具體實(shí)踐聯(lián)想:
在公司中有一個(gè)流程是課程的報(bào)名那么他會(huì)有多種狀態(tài),例如結(jié)束忿偷,開始金顿,報(bào)名成功,報(bào)名成功和支付成功等鲤桥,針對(duì)這些不同的狀態(tài)具體的操作又不一樣揍拆,同時(shí)還有包含的關(guān)系,例如開始報(bào)名的話茶凳,就需要報(bào)名嫂拴,和支付兩個(gè)狀態(tài)同時(shí)出現(xiàn),這個(gè)剛好進(jìn)入的狀態(tài)是開始報(bào)名贮喧,當(dāng)報(bào)名成功后將狀態(tài)變?yōu)閳?bào)名成功筒狠,在調(diào)用報(bào)名成功狀態(tài)下的方法,這樣既可以將各個(gè)操作簡(jiǎn)單的寫一次箱沦,也可以將各個(gè)操作分開處理辩恼,利于代碼的梳理。