iOS App的設(shè)計(jì)模式開(kāi)發(fā)中對(duì)State狀態(tài)模式的運(yùn)用
1.概述
在軟件開(kāi)發(fā)過(guò)程中,應(yīng)用程序可能會(huì)根據(jù)不同的情況作出不同的處理。最直接的解決方案是將這些所有可能發(fā)生的情況全都考慮到。然后使用if... ellse語(yǔ)句來(lái)做狀態(tài)判斷來(lái)進(jìn)行不同情況的處理。但是對(duì)復(fù)雜狀態(tài)的判斷就顯得“力不從心了”。隨著增加新的狀態(tài)或者修改一個(gè)狀體(if else(或switch case)語(yǔ)句的增多或者修改)可能會(huì)引起很大的修改,而程序的可讀性,擴(kuò)展性也會(huì)變得很弱。維護(hù)也會(huì)很麻煩。那么我就考慮只修改自身狀態(tài)的模式。
例子1:按鈕來(lái)控制一個(gè)電梯的狀態(tài),一個(gè)電梯開(kāi)們,關(guān)門(mén),停,運(yùn)行。每一種狀態(tài)改變,都有可能要根據(jù)其他狀態(tài)來(lái)更新處理。例如,開(kāi)門(mén)狀體,你不能在運(yùn)行的時(shí)候開(kāi)門(mén),而是在電梯定下后才能開(kāi)門(mén)。
例子2:我們給一部手機(jī)打電話,就可能出現(xiàn)這幾種情況:用戶開(kāi)機(jī),用戶關(guān)機(jī),用戶欠費(fèi)停機(jī),用戶消戶等。 所以當(dāng)我們撥打這個(gè)號(hào)碼的時(shí)候:系統(tǒng)就要判斷,該用戶是否在開(kāi)機(jī)且不忙狀態(tài),又或者是關(guān)機(jī),欠費(fèi)等狀態(tài)。但不管是那種狀態(tài)我們都應(yīng)給出對(duì)應(yīng)的處理操作。
2.問(wèn)題
對(duì)象如何在每一種狀態(tài)下表現(xiàn)出不同的行為?
3.解決方案
狀態(tài)模式:允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變時(shí)改變它的行為。對(duì)象看起來(lái)似乎修改了它的類(lèi)。
在很多情況下,一個(gè)對(duì)象的行為取決于一個(gè)或多個(gè)動(dòng)態(tài)變化的屬性,這樣的屬性叫做狀態(tài),這樣的對(duì)象叫做有狀態(tài)的(stateful)對(duì)象,這樣的對(duì)象狀態(tài)是從事先定義好的一系列值中取出的。當(dāng)一個(gè)這樣的對(duì)象與外部事件產(chǎn)生互動(dòng)時(shí),其內(nèi)部狀態(tài)就會(huì)改變,從而使得系統(tǒng)的行為也隨之發(fā)生變化。
4.示例
先給出這個(gè)例子的類(lèi)結(jié)構(gòu)圖。
上面的類(lèi)結(jié)構(gòu)圖并不復(fù)雜,首先是抽象出一個(gè)狀態(tài)的父類(lèi),通過(guò)工作類(lèi)對(duì)時(shí)間點(diǎn)的設(shè)置來(lái)切換不同的狀態(tài)。
邏輯結(jié)構(gòu)并不復(fù)雜,還是給出簡(jiǎn)易的代碼,大家可以慢慢體會(huì)一下。
注意:本文所有代碼均在ARC環(huán)境下編譯通過(guò)。
Work類(lèi)接口
#import <Foundation/Foundation.h>
@class State;
@interface Work :NSObject{
State *current;
}
@property double Hour;
@property BOOL TaskFinished;
-(void)SetState:(State*)s;
-(void)WriteProgram;
@end
Work類(lèi)實(shí)現(xiàn)
#import "Work.h"
#import "State.h"
#import "ForenoonState.h"
@implementation Work
@synthesize Hour =_Hour;
@synthesize TaskFinished =_TaskFinished;
-(id)init{
if (self == [superinit]) {
current= [[ForenoonState alloc]init];
}
return self;
}
-(void)SetState:(State *)s{
current = s;
}
-(void)WriteProgram{
[current WriteProgram:self];
}
@end
State類(lèi)接口
#import <Foundation/Foundation.h>
@class Work;
@interface State:NSObject
-(void)WriteProgram:(Work*)w;
@end
State類(lèi)實(shí)現(xiàn)
#import "State.h"
#import "Work.h"
@implementation State
-(void)WriteProgram:(Work *)w{
NSLog(@"當(dāng)前時(shí)間:%f點(diǎn)下班回家了",[w Hour]);
}
@end
ForenoonState類(lèi)接口
#import "State.h"
@interface ForenoonState :State
@end
ForenoonState類(lèi)實(shí)現(xiàn)
#import "ForenoonState.h"
#import "Work.h"
#import "NoonState.h"
@implementation ForenoonState
-(void)WriteProgram:(Work *)w{
if ([w Hour] < 12) {
NSLog(@"當(dāng)前時(shí)間:%f點(diǎn)上午工作,精神百倍", [w Hour]);
}
else {
[w SetState:[NoonState new]];
[w WriteProgram];
}
}
@end
NoonState類(lèi)接口
#import "State.h"
@interface NoonState:State
@end
NoonState類(lèi)實(shí)現(xiàn)
#import "NoonState.h"
#import "Work.h"
#import "AfternoonState.h"
@implementation NoonState
-(void)WriteProgram:(Work *)w{
if([w Hour] <13)
NSLog(@"當(dāng)前時(shí)間:%f點(diǎn)餓了,午飯;犯困,午休",[w Hour]);
else {
[w SetState:[[AfternoonState alloc]init]];
[w WriteProgram];
}
}
@end
AfternoonState類(lèi)接口
#import "State.h"
@interface AfternoonState :State
@end
AfternoonState類(lèi)實(shí)現(xiàn)
#import "AfternoonState.h"
#import "Work.h"
#import "EveningState.h"
@implementation AfternoonState
-(void)WriteProgram:(Work *)w{
if ([w Hour] <17) {
NSLog(@"當(dāng)前時(shí)間:%f點(diǎn)下午狀態(tài)還不錯(cuò),繼續(xù)努力", [w Hour]);
}
else {
[w SetState:[[EveningState alloc]init]];
[w WriteProgram];
}
}
@end
EveningState類(lèi)接口
#import "State.h"
@interface EveningState:State
@end
EveningState類(lèi)實(shí)現(xiàn)
#import "EveningState.h"
#import "Work.h"
#import "RestState.h"
#import "SleepingState.h"
@implementation EveningState
-(void)WriteProgram:(Work *)w{
if ([w TaskFinished]) {
[w SetState:[[RestState alloc]init]];
[w WriteProgram];
}
else {
if([w Hour] <21)
NSLog(@"當(dāng)前時(shí)間:%f點(diǎn)加班哦,疲憊之極", [w Hour]);
else {
[w SetState:[[SleepingState alloc]init]];
[w WriteProgram];
}
}
}
@end
SleepingState類(lèi)接口
#import "State.h"
@interface SleepingState :State
@end
SleepingState類(lèi)實(shí)現(xiàn)
#import "SleepingState.h"
#import "Work.h"
@implementation SleepingState
-(void)WriteProgram:(Work *)w{
NSLog(@"當(dāng)前時(shí)間:%f點(diǎn)不行了,睡著了", [w Hour]);
}
@end
RestState類(lèi)接口
#import "RestState.h"
#import "Work.h"
@implementation RestState
-(void)WriteProgram:(Work *)w{
NSLog(@"當(dāng)前時(shí)間:%f點(diǎn)下班回家了", [w Hour]);
}
@end
Main方法調(diào)用
#import <Foundation/Foundation.h>
#import "Work.h"
int main (int argc,const char *argv[])
{
@autoreleasepool{
Work *emergencyProjects = [[Work alloc]init];
[emergencyProjects setHour:9];
[emergencyProjects WriteProgram];
[emergencyProjects setHour:10];
[emergencyProjects WriteProgram];
[emergencyProjects setHour:12];
[emergencyProjects WriteProgram];
[emergencyProjects setHour:13];
[emergencyProjects WriteProgram];
[emergencyProjects setHour:14];
[emergencyProjects WriteProgram];
[emergencyProjects setHour:17];
[emergencyProjects WriteProgram];
[emergencyProjects setTaskFinished:NO];
[emergencyProjects setHour:19];
[emergencyProjects WriteProgram];
[emergencyProjects setHour:22];
[emergencyProjects WriteProgram];
}
return 0;
}
上面是用Objective C語(yǔ)言實(shí)現(xiàn)的簡(jiǎn)單代碼。
通過(guò)這個(gè)例子,可以看到,狀態(tài)模式通過(guò)把各種狀態(tài)轉(zhuǎn)移邏輯分布到State的子類(lèi)之間,來(lái)減少相互間的依賴。當(dāng)一個(gè)對(duì)象的行為取決于它的狀態(tài),并且它必須在運(yùn)行時(shí)刻根據(jù)狀態(tài)改變它的行為時(shí),就可以考慮使用狀態(tài)模式了。
5.適用性
在下面的兩種情況下均可使用State模式:
1) • 一個(gè)對(duì)象的行為取決于它的狀態(tài), 并且它必須在運(yùn)行時(shí)刻根據(jù)狀態(tài)改變它的行為。
2) • 代碼中包含大量與對(duì)象狀態(tài)有關(guān)的條件語(yǔ)句:一個(gè)操作中含有龐大的多分支的條件(if else(或switch case)語(yǔ)句,且這些分支依賴于該對(duì)象的狀態(tài)。這個(gè)狀態(tài)通常用一個(gè)或多個(gè)枚舉常量表示。通常 , 有多個(gè)操作包含這一相同的條件結(jié)構(gòu)。 State模式將每一個(gè)條件分支放入一個(gè)獨(dú)立的類(lèi)中。這使得你可以根據(jù)對(duì)象自身的情況將對(duì)象的狀態(tài)作為一個(gè)對(duì)象,這一對(duì)象可以不依賴于其他對(duì)象而獨(dú)立變化。
6.結(jié)構(gòu)
7.模式的組成
環(huán)境類(lèi)(Context): 定義客戶感興趣的接口。維護(hù)一個(gè)ConcreteState子類(lèi)的實(shí)例,這個(gè)實(shí)例定義當(dāng)前狀態(tài)。
抽象狀態(tài)類(lèi)(State): 定義一個(gè)接口以封裝與Context的一個(gè)特定狀態(tài)相關(guān)的行為。
具體狀態(tài)類(lèi)(ConcreteState): 每一子類(lèi)實(shí)現(xiàn)一個(gè)與Context的一個(gè)狀態(tài)相關(guān)的行為。
8.效果
State模式有下面一些效果:
狀態(tài)模式的優(yōu)點(diǎn):
1 ) 它將與特定狀態(tài)相關(guān)的行為局部化,并且將不同狀態(tài)的行為分割開(kāi)來(lái): State模式將所有與一個(gè)特定的狀態(tài)相關(guān)的行為都放入一個(gè)對(duì)象中。因?yàn)樗信c狀態(tài)相關(guān)的代碼都存在于某一個(gè)State子類(lèi)中, 所以通過(guò)定義新的子類(lèi)可以很容易的增加新的狀態(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子類(lèi)中。這就增加了子類(lèi)的數(shù)目,相對(duì)于單個(gè)類(lèi)的實(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子類(lèi)之間。將每一個(gè)狀態(tài)轉(zhuǎn)換和動(dòng)作封裝到一個(gè)類(lèi)中,就把著眼點(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ì)象沒(méi)有實(shí)例變量—即它們表示的狀態(tài)完全以它們的類(lèi)型來(lái)編碼—那么各Context對(duì)象可以共享一個(gè)State對(duì)象。當(dāng)狀態(tài)以這種方式被共享時(shí), 它們必然是沒(méi)有內(nèi)部狀態(tài), 只有行為的輕量級(jí)對(duì)象。
狀態(tài)模式的缺點(diǎn):
1) 狀態(tài)模式的使用必然會(huì)增加系統(tǒng)類(lèi)和對(duì)象的個(gè)數(shù)。
2) 狀態(tài)模式的結(jié)構(gòu)與實(shí)現(xiàn)都較為復(fù)雜,如果使用不當(dāng)將導(dǎo)致程序結(jié)構(gòu)和代碼的混亂。
- iOS App開(kāi)發(fā)中使用設(shè)計(jì)模式中的單例模式的實(shí)例解析
- 詳解iOS應(yīng)用的設(shè)計(jì)模式開(kāi)發(fā)中Mediator中介者模式的使用
- iOS App設(shè)計(jì)模式開(kāi)發(fā)中對(duì)迭代器模式的使用示例
- 實(shí)例解析設(shè)計(jì)模式中的外觀模式在iOS App開(kāi)發(fā)中的運(yùn)用
- 深入解析設(shè)計(jì)模式中的裝飾器模式在iOS應(yīng)用開(kāi)發(fā)中的實(shí)現(xiàn)
- iOS應(yīng)用運(yùn)用設(shè)計(jì)模式中的Strategy策略模式的開(kāi)發(fā)實(shí)例
- iOS App設(shè)計(jì)模式開(kāi)發(fā)中策略模式的實(shí)現(xiàn)示例
- IOS開(kāi)發(fā)中的設(shè)計(jì)模式匯總
相關(guān)文章
iOS App開(kāi)發(fā)中通過(guò)UIDevice類(lèi)獲取設(shè)備信息的方法
UIDevice最常見(jiàn)的用法就是用來(lái)監(jiān)測(cè)iOS設(shè)備的電量了,然后再實(shí)現(xiàn)電池狀態(tài)通知非常方便,除此之外還有傳感器等信息的獲取,這里我們就來(lái)總結(jié)一下iOS App開(kāi)發(fā)中通過(guò)UIDevice類(lèi)獲取設(shè)備信息的方法:2016-07-07iOS實(shí)現(xiàn)文字轉(zhuǎn)化成彩色文字圖片
這篇文章主要為大家詳細(xì)介紹了iOS文字轉(zhuǎn)化成彩色文字圖片的實(shí)現(xiàn)方法,可以實(shí)現(xiàn)不同字體,漸變的效果,感興趣的小伙伴們可以參考一下2016-03-03Objective-C與Swift之間的互相調(diào)用和跳轉(zhuǎn)
這篇文章主要給大家介紹了關(guān)于Objective-C與Swift之間的互相調(diào)用和跳轉(zhuǎn)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05iOS UIAlertController中UITextField添加晃動(dòng)效果與邊框顏色詳解
這篇文章主要給大家介紹了關(guān)于iOS UIAlertController中UITextField添加晃動(dòng)效果與邊框顏色的相關(guān)資料,實(shí)現(xiàn)后的效果非常適合在開(kāi)發(fā)中使用,文中給出了詳細(xì)的示例代碼,需要的朋友可以參考借鑒,下面隨著小編來(lái)一起看看吧。2017-10-10iOS通過(guò)攝像頭圖像識(shí)別技術(shù)分享
本篇文章給大家詳細(xì)講述了讓IOS開(kāi)發(fā)中通過(guò)攝像頭進(jìn)行圖像識(shí)別的相關(guān)技術(shù),對(duì)此有興趣的朋友參考學(xué)習(xí)下吧。2018-02-02