状态模式
意图:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的情况。把状态的判断逻辑转移到表现不同状态的一系列类当中,可以把复杂的判断逻辑简化。
状态模式是状态机
的一种实现方式。针对状态机的实现方式有:分支逻辑法、查表法和状态模式。
状态机又叫有限状态机
,Finite State Machine,FSM它有3部分组成:状态、事件、动作。其中事件也称为转移条件。事件触发状态的转移机动作的执行。不过,动作不是必须的,也可能只转移状态,不执行任何动作。

角色和职责
- Context 用户对象、环境、上下文
拥有一个State类型的成员,以标识对象的当前状态
负责具体状态的切换 - State 状态
接口或基类
封装与Context的特定状态相关的行为 - ConcreteState 接口实现类或子类
实现了一个与Context某个状态相关的行为
代码示例
//代码意图演示工人在不同的时间对应不同的状态
//7点或8点的是吃早餐的状态,9点或10点是工作状态,其他情况未知状态
//Worker.h
#import <Foundation/Foundation.h>
@class State;
NS_ASSUME_NONNULL_BEGIN
@interface Worker : NSObject
@property (nonatomic, assign) NSInteger hour;
@property (nonatomic, strong) State *currentState;
- (void)doSomething;
@end
NS_ASSUME_NONNULL_END
//Worker.m
#import "Worker.h"
#import "StateA.h"
@implementation Worker
- (instancetype)init {
if (self = [super init]) {
self.currentState = [[StateA alloc] init];
}
return self;
}
- (void)doSomething {
[self.currentState doSomething:self];
}
@end
//State.h
#import <Foundation/Foundation.h>
@class Worker;
NS_ASSUME_NONNULL_BEGIN
@interface State : NSObject
- (void)doSomething:(Worker *)worker;
@end
NS_ASSUME_NONNULL_END
//State.m
#import "State.h"
@implementation State
- (void)doSomething:(Worker *)worker {
}
@end
//StateA.h
#import "State.h"
NS_ASSUME_NONNULL_BEGIN
@interface StateA : State
@end
NS_ASSUME_NONNULL_END
//StateA.m
#import "StateA.h"
#import "Worker.h"
#import "StateB.h"
@implementation StateA
- (void)doSomething:(Worker *)worker {
if (worker.hour == 7 || worker.hour == 8) {
NSLog(@"吃早饭的状态");
}else {
worker.currentState = [[StateB alloc] init];
[worker.currentState doSomething:worker];
}
}
@end
//StateB.h
#import "State.h"
NS_ASSUME_NONNULL_BEGIN
@interface StateB : State
@end
NS_ASSUME_NONNULL_END
//StateB.m
#import "StateB.h"
#import "Worker.h"
#import "StateA.h"
@implementation StateB
- (void)doSomething:(Worker *)worker {
if (worker.hour == 9 || worker.hour == 10) {
NSLog(@"工作的状态");
}else {
worker.currentState = [[StateA alloc] init];
NSLog(@"当前时间:%ld+++未知状态", worker.hour);
}
}
@end
//main.m
#import "Worker.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Worker *worker = [[Worker alloc] init];
worker.hour = 7;
[worker doSomething];
worker.hour = 9;
[worker doSomething];
}
return 0;
}
/*
吃早饭的状态
工作的状态
*/
用处
每个人、事物在不同的状态下会有不同的表现(动作),而一个状态又会在不同的表现下转移到下一个不同的状态(State)。最简单的一个生活中的例子就是:地铁入口处,如果你放入正确的地铁票,门就会打开让你通过。在出口处也是验票,如果正确你就可以通过,否则就不让你通过(如果你动作野蛮,或许会有报警(Alarm))。
状态模式适合状态不多,状态转移也比较简单,但事件触发执行的动作包含的业务逻辑可能会比较复杂的场景。
通过用户的状态来改变对象的行为。
优点
- 结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一原则”。
- 将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确。
- 状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
缺点
状态模式的使用类似策略模式,必然会增加系统的类和对象的个数。
题外话
分支逻辑法:通常我们在实现这类系统会使用到很多switch/case语句,case某种状态,发生什么动作,case另一种状态,则发生另外一种状态,但是这种方式至少有以下两个问题:
- 当状态数据不是很多的时候,switch/case可能可以搞定。但是当状态数目很多的时候(实际系统重也正是如此),维护一个大组switch/case语句将是一件异常困难并且容易出错的事情。
- 状态逻辑和动作实现没有分离。在很多的系统实现中,动作的实现代码直接写在状态的逻辑当中。这带来的后果就是系统的扩展性和维护得不到保证。
状态模式和策略模式的UML结构图非常像,区别和联系参考:状态模式和策略模式的区别与联系?
对比代码实现也可看出:策略模式的意图是让算法
相互替换,状态模式的意图是改变一个对象
的内部状态改变。
网友评论