状态模式(State Pattern),当一个对象的现下状态改变时,允许改变其行为,这个对象看起来像是改变了其类
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况,把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化
场景
在线投票系统,要实现控制同一个用户只能投一票,如果一个用户反复投票,而且投票此时超过3次,则判定为恶意刷票,要取消用户投票的资格和他所投的票.如果一个用户的投票次数超过5次,将进入黑名单,则禁止再登录和使用系统
怎么实现这样的功能呢?,先分析一下上面的功能,需要记录用户投票的记录(是否投票),同时还要记录用户投票的次数,主要有四种情况
- 用户正常投票
- 用户正常投票后,有意或者无意的重复投票
- 用户恶意投票
- 黑名单用户
要处理这样的情况,第一反应是在逻辑里写一堆if else判断去区分,但这么写大家想想是不是有问题?
- 如果要修改某种投票情况所对应的具体处理方式,那就需要在一堆判断力,找到相应的代码,然后进行改动
- 如果要添加新的功能,比如比如投票超过5次但不足8次的,给个机会,只是禁止登录和使用系统3天,如果再犯,才永久封掉账号,该怎么办呢?那就需要改动投票管理的源代码,再添加一个if else
不管哪一种情况,都是在一大堆的控制代码找出重要的代码,然后进行修改,这不是好方法,那么该如何实现才能做到:既能够很容易的给方法添加新的功能,又能够很方便的修改已有的功能处理呢?
解决方案
用来解决上述问题的一个合理的解决方案就是状态模式
应用状态模式来解决的思路
仔细分析上面的问题,用户投票的类型就相当于描述了人员的几种投票状态,而各个状态和对应的功能处理具有很强的对应性,有点类似于“一个萝卜一个坑”,各个状态下的处理基本上都是不一样的,也不存在可以相互替换的可能。
为了解决上面提出的问题,很自然的一个设计就是首先把状态和状态对应的行为从原来的大杂烩代码中分离出来,把每个状态所对应的功能处理封装在一个独立的类里面,这样选择不同处理的时候,其实就是在选择不同的状态处理类.
然后为了统一操作这些不同的状态类,定义一个状态接口来约束它们,这样外部就可以面向这个统一的状态接口编程,而无需关心具体的状态类实现了。
这样一来,要修改某种投票情况所对应的具体功能处理,那就是直接修改或者扩展某个状态处理类的功能就可以了。而要添加新的功能就更简单,直接添加新的状态处理类就可以了,当然在使用Context的时候,需要设置使用这个新的状态类的实例。
模式结构和说明
Context:环境,也称上下文,通常用来定义客户感兴趣的接口,同时维护一个来具体处理当前状态的实例对象。
State:状态接口,用来封装与上下文的一个特定状态所对应的行为。
ConcreteState:具体实现状态处理的类,每个类实现一个跟上下文相关的状态的具体处理。
示例代码
- 先定义一个抽象投票类(定义投票的接口)
@interface VoteState : NSObject
- (void)voteStateWithUser:(NSString *)user;
@end
- 定义状态处理类(这儿只给出其中两个)
@implementation NormalState
- (void)voteStateWithUser:(NSString *)user
{
NSLog(@"正常投票");
}
@end
@implementation RepeatState
- (void)voteStateWithUser:(NSString *)user
{
NSLog(@"不要重复投票");
}
@end
- 实现投票管理类
@implementation VoteManager
- (void)vote
{
static NSInteger oldCount = 0;
oldCount += 1;
VoteState *state = nil;
if (oldCount == 1) {
state = [NormalState new];
} else if (oldCount > 1 && oldCount < 3){
state = [RepeatState new];
}else if (oldCount >= 3 && oldCount < 5){
state = [SpiteState new];
} else if (oldCount >= 5){
state = [BlackState new];
}
[state voteStateWithUser:@"张三"];
}
@end
各个状态使用不同的处理类,具体的处理由类去实现,并且是统一的接口实现
4. 客户端实现
VoteManager *manager = [VoteManager new];
for (int i = 0; i < 5; i++) {
[manager vote];
}
模式讲解
1. 状态和行为
所谓对象的状态,通常指的就是对象实例的属性的值,而行为指的就是对象的功能,在具体点,行为多半可以对应到方法上.
状态模式的功能就是分离状态的行为,通过维护状态的变化,来调用不同的状态对应的不同的功能。也就是说,状态和行为是相关联的,它们的关系可以描述为:状态决定行为。
由于状态是在运行期被改变的,因此行为也会在运行期,根据状态的改变而改变,看起来,同一个对象,在不同的运行时刻,行为是不一样的,就像是类被修改了一样
2. 行为的平行性
注意是平行性而不是平等性。所谓平行性指的是各个状态的行为所处的层次是一样的,相互是独立的、没有关联的,是根据不同的状态来决定到底走平行线的那一条,行为是不同的,当然对应的实现也是不同的,相互之间是不可替换的。
模式的缺点
由于状态的维护和转换在状态模式结构里面,不管你是扩展了状态实现类,还是新添加了状态实现类,都需要修改状态维护和转换的地方,以使用新的实现。也就是必须修改voteManager类,并没有遵循OCP原则
当然在状态固定的情况下,大家的状态处理类实现方式可以使用责任链模式,那么在管理类里面就不需要一堆if else判断了,具体如何实现,大家试试,不懂的私聊