参考博客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) 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
具体实践联想:
在公司中有一个流程是课程的报名那么他会有多种状态,例如结束,开始,报名成功,报名成功和支付成功等,针对这些不同的状态具体的操作又不一样,同时还有包含的关系,例如开始报名的话,就需要报名,和支付两个状态同时出现,这个刚好进入的状态是开始报名,当报名成功后将状态变为报名成功,在调用报名成功状态下的方法,这样既可以将各个操作简单的写一次,也可以将各个操作分开处理,利于代码的梳理。