状态模式

.概述

在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理。最直接的解决方案是将这些所有可能发生的情况全都考虑到。然后使用if... ellse语句来做状态判断来进行不同情况的处理。但是对复杂状态的判断就显得“力不从心了”。随着增加新的状态或者修改一个状体(if else(或switch case)语句的增多或者修改)可能会引起很大的修改,而程序的可读性,扩展性也会变得很弱。维护也会很麻烦。那么我就考虑只修改自身状态的模式。

例子1:按钮来控制一个电梯的状态,一个电梯开们,关门,停,运行。每一种状态改变,都有可能要根据其他状态来更新处理。例如,开门状体,你不能在运行的时候开门,而是在电梯定下后才能开门。

例子2:我们给一部手机打电话,就可能出现这几种情况:用户开机,用户关机,用户欠费停机,用户消户等。 所以当我们拨打这个号码的时候:系统就要判断,该用户是否在开机且不忙状态,又或者是关机,欠费等状态。但不管是那种状态我们都应给出对应的处理操作。

2.问题

对象如何在每一种状态下表现出不同的行为?

3.解决方案

状态模式:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的(stateful)对象,这样的对象状态是从事先定义好的一系列值中取出的。当一个这样的对象与外部事件产生互动时,其内部状态就会改变,从而使得系统的行为也随之发生变化。

4.示例

先给出这个例子的类结构图。

上面的类结构图并不复杂,首先是抽象出一个状态的父类,通过工作类对时间点的设置来切换不同的状态。

逻辑结构并不复杂,还是给出简易的代码,大家可以慢慢体会一下。

注意:本文所有代码均在ARC环境下编译通过。

Work类接口

复制代码代码如下:

#import

@class State;

@interface Work :NSObject{

State *current;

}

@property double Hour;

@property BOOL TaskFinished;

-(void)SetState:(State*)s;

-(void)WriteProgram;

@end

Work类实现

复制代码代码如下:

#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类接口

复制代码代码如下:

#import

@class Work;

@interface State:NSObject

-(void)WriteProgram:(Work*)w;

@end

State类实现

复制代码代码如下:

#import "State.h"

#import "Work.h"

@implementation State

-(void)WriteProgram:(Work *)w{

NSLog(@"当前时间:%f点下班回家了",[w    Hour]);

}

@end

ForenoonState类接口

复制代码代码如下:

#import    "State.h"

@interface    ForenoonState :State

@end

ForenoonState类实现

复制代码代码如下:

#import "ForenoonState.h"

#import "Work.h"

#import "NoonState.h"

@implementation ForenoonState

-(void)WriteProgram:(Work *)w{

if ([w Hour] < 12) {

NSLog(@"当前时间:%f点上午工作,精神百倍", [w Hour]);

}

else {

[w SetState:[NoonState new]];

[w WriteProgram];

}

}

@end

NoonState类接口

复制代码代码如下:

#import "State.h"

@interface NoonState:State

@end

NoonState类实现

复制代码代码如下:

#import "NoonState.h"

#import "Work.h"

#import "AfternoonState.h"

@implementation NoonState

-(void)WriteProgram:(Work *)w{

if([w Hour] <13)

NSLog(@"当前时间:%f点饿了,午饭;犯困,午休",[w Hour]);

else {

[w SetState:[[AfternoonState    alloc]init]];

[w WriteProgram];

}

}

@end

AfternoonState类接口

复制代码代码如下:

#import "State.h"

@interface AfternoonState :State

@end

AfternoonState类实现

复制代码代码如下:

#import "AfternoonState.h"

#import "Work.h"

#import "EveningState.h"

@implementation AfternoonState

-(void)WriteProgram:(Work *)w{

if ([w Hour] <17) {

NSLog(@"当前时间:%f点下午状态还不错,继续努力", [w Hour]);

}

else {

[w SetState:[[EveningState alloc]init]];

[w WriteProgram];

}

}

@end

EveningState类接口

复制代码代码如下:

#import "State.h"

@interface EveningState:State

@end

EveningState类实现

复制代码代码如下:

#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(@"当前时间:%f点加班哦,疲惫之极", [w Hour]);

else {

[w SetState:[[SleepingState alloc]init]];

[w WriteProgram];

}

}

}

@end

SleepingState类接口

复制代码代码如下:

#import "State.h"

@interface SleepingState :State

@end

SleepingState类实现

复制代码代码如下:

#import "SleepingState.h"

#import "Work.h"

@implementation SleepingState

-(void)WriteProgram:(Work *)w{

NSLog(@"当前时间:%f点不行了,睡着了", [w Hour]);

}

@end

RestState类接口

复制代码代码如下:

#import "RestState.h"

#import "Work.h"

@implementation RestState

-(void)WriteProgram:(Work *)w{

NSLog(@"当前时间:%f点下班回家了", [w Hour]);

}

@end

Main方法调用

复制代码代码如下:

#import

#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语言实现的简单代码。

通过这个例子,可以看到,状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖。当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。

5.适用性

在下面的两种情况下均可使用State模式:

1) • 一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。

2) • 代码中包含大量与对象状态有关的条件语句:一个操作中含有庞大的多分支的条件(if else(或switch case)语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常 , 有多个操作包含这一相同的条件结构。 State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。

6.结构

7.模式的组成

环境类(Context):  定义客户感兴趣的接口。维护一个ConcreteState子类的实例,这个实例定义当前状态。

抽象状态类(State):  定义一个接口以封装与Context的一个特定状态相关的行为。

具体状态类(ConcreteState):  每一子类实现一个与Context的一个状态相关的行为。

8.效果

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) 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,783评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,360评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,942评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,507评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,324评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,299评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,685评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,358评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,652评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,704评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,465评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,318评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,711评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,991评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,265评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,661评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,864评论 2 335

推荐阅读更多精彩内容