状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。
状态模式的优点
● 结构清晰
避免了过多的switch...case或者if...else语句的使用,避免了程序的复杂性,提高系统的可维护性。
● 遵循设计原则
很好地体现了开闭原则和单一职责原则,每个状态都是一个子类,你要增加状态就要增加子类,你要修改状态,你只修改一个子类就可以了。
● 封装性非常好
这也是状态模式的基本要求,状态变换放置到类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的变换
状态模式的缺点
● 但只有一个缺点,子类会太多,也就是类膨胀。
环境角色有两个不成文的约束:
● 把状态对象声明为静态常量,有几个状态对象就声明几个静态常量。
● 环境角色具有状态抽象角色定义的所有行为,具体执行使用委托方式。
上面内容来自《设计模式之禅》java版本的。那么OC怎么实现呢?OC中怎么来声明静态常量呢?我们看一个电梯的例子:
电梯有开门、关门、运行、停止。然后在实现类中电梯的每一次动作发生都要对状态进行判断,判断是否可以执行,也就是动作的执行是否符合业务逻辑。
电梯的状态
开门状态,电梯是关门或停止状态可以开门。
关门状态,电梯是开门状态可以关门。
运行状态,电梯是关门或停止状态可以运行。
停止状态,电梯是运行或关门的状态可以停止。
如果直接实现,代码是这个样子:
typedef enum : NSInteger{
LiftStateOpenType,
LiftStateCloseType,
LiftStateRunType,
LiftStateStopType,
}LiftStateType;
@interface StateMain()
@property (nonatomic,assign) LiftStateType stateType;
@end
@implementation StateMain
- (void)main{
[self open];
[self close];
[self run];
[self stop];
}
//关门或停止的时候可以运行
- (void)run{
if (_stateType == LiftStateCloseType || _stateType == LiftStateStopType) {
_stateType = LiftStateRunType;
[self runWithoutLogic];
}
}
//关门或停止的时候可以开门
- (void)open{
if (_stateType == LiftStateCloseType || _stateType == LiftStateStopType) {
_stateType = LiftStateOpenType;
[self openWithoutLogic];
}
}
//开门的时候才可以关门
- (void)close{
if (_stateType == LiftStateOpenType) {
_stateType = LiftStateCloseType;
[self closeWithoutLogic];
}
}
//运行或关门状态可以停止
- (void)stop{
if (_stateType == LiftStateCloseType || _stateType == LiftStateRunType) {
_stateType = LiftStateStopType;
[self stopWithoutLogic];
}
}
//纯粹的电梯关门,不考虑实际的逻辑
-(void)closeWithoutLogic{
NSLog(@"电梯门关闭...");
}
//纯粹的电梯开门,不考虑任何条件
-(void)openWithoutLogic{
NSLog(@"电梯门开启...");
}
//纯粹的运行,不考虑其他条件
-(void)runWithoutLogic{
NSLog(@"电梯上下运行起来...");
}
//单纯的停止,不考虑其他条件
-(void)stopWithoutLogic{
NSLog(@"电梯停止了...");
}
@end
首先必须得屡出各种判断的状态,用if条件加以限制。问题不大,但如果这是业务呢?我觉得能屡各种状态,没有bug,已经很厉害了。这里已经有四种状态了,如果业务再增加两种呢?你是不是得重头再屡一遍。头是不是更大。
怎么能控制这些状态呢?试试状态模式,下面看下代码。
1.声明一个电梯的状态类,再加上一些抽象状态的方法,这些方法实现交给子类实现。
@interface LiftState : NSObject
- (id)initWithContext:(StateContext *)context;
- (StateContext *)getStateContext;
@end
@interface LiftState(Abstract)
- (void)open;
- (void)close;
- (void)run;
- (void)stop;
@end
2.子类方法实现
//开门状态的类,这个时候可以关门。
@interface OpenningState : LiftState
@end
#import "OpenningState.h"
#import "StateContext.h"
@implementation OpenningState
- (void)close{
StateContext * _stateContext = [self getStateContext];
[_stateContext setLiftState:_stateContext.closingState];
[[_stateContext liftState] close];
}
- (void)open{
NSLog(@"电梯门开启");
}
- (void)run{
}
- (void)stop{
}
@end
//关门状态的类,这个时候可以开门、运行、停止
@interface ClosingState : LiftState
@end
#import "ClosingState.h"
#import "StateContext.h"
@implementation ClosingState
- (void)close{
NSLog(@"电梯门关闭");
}
- (void)open{
StateContext * _stateContext = [self getStateContext];
[_stateContext setLiftState:_stateContext.openingState];
[[_stateContext liftState] open];
}
- (void)run{
StateContext * _stateContext = [self getStateContext];
[_stateContext setLiftState:_stateContext.runningState];
[[_stateContext liftState] run];
}
- (void)stop{
StateContext * _stateContext = [self getStateContext];
[_stateContext setLiftState:_stateContext.stoppingState];
[[_stateContext liftState] stop];
}
@end
//运行状态的类,这个时候可以停止的
@interface RunningState : LiftState
@end
@implementation RunningState
- (void)close{
}
- (void)open{
}
- (void)run{
NSLog(@"电梯上下运行");
}
- (void)stop{
StateContext * _stateContext = [self getStateContext];
[_stateContext setLiftState:_stateContext.stoppingState];
[[_stateContext liftState] stop];
}
@end
//停止状态,这个时候可以开门、运行的
@interface StoppingState : LiftState
@end
@implementation StoppingState
- (void)close{
}
- (void)open{
StateContext * _stateContext = [self getStateContext];
[_stateContext setLiftState:_stateContext.openingState];
[[_stateContext liftState] open];
}
- (void)run{
StateContext * _stateContext = [self getStateContext];
[_stateContext setLiftState:_stateContext.runningState];
[[_stateContext liftState] run];
}
- (void)stop{
NSLog(@"电梯停止了");
}
@end
3.上面都有一个惯串的类StateContext,这也是各种状态切换的中枢神经。
#import <Foundation/Foundation.h>
@class OpenningState;
@class ClosingState;
@class RunningState;
@class StoppingState;
@class LiftState;
NS_ASSUME_NONNULL_BEGIN
@interface StateContext : NSObject
@property (nonatomic,strong,readonly) OpenningState * openingState;
@property (nonatomic,strong,readonly) ClosingState * closingState;
@property (nonatomic,strong,readonly) RunningState * runningState;
@property (nonatomic,strong,readonly) StoppingState * stoppingState;
@property (nonatomic,strong) LiftState * liftState;
- (void)open;
- (void)close;
- (void)run;
- (void)stop;
@end
NS_ASSUME_NONNULL_END
#import "StateContext.h"
#import "OpenningState.h"
#import "ClosingState.h"
#import "StoppingState.h"
#import "RunningState.h"
@interface StateContext()
@end
@implementation StateContext
- (instancetype)init{
self = [super init];
if (self) {
_openingState = [[OpenningState alloc] initWithContext:self];
_closingState = [[ClosingState alloc] initWithContext:self];
_runningState = [[RunningState alloc] initWithContext:self];
_stoppingState = [[StoppingState alloc] initWithContext:self];
[self setLiftState:_closingState];
}
return self;
}
- (void)setLiftState:(LiftState *)liftState{
_liftState = liftState;
}
- (void)open{
[_liftState open];
}
- (void)close{
[_liftState close];
}
- (void)run{
[_liftState run];
}
- (void)stop{
[_liftState stop];
}
- (void)dealloc{
}
@end
4.调用各种状态
StateContext * context = [StateContext new];
[context open];
[context close];
[context run];
[context stop];
回到前面的问题:
1.oc没有java 中的static final常量,所以我把StateContext传到各个子类中,解决常量的问题。
2.如果还要增加子类,仅需要继承自LiftState类,做一些修改。至少不会影响前面的逻辑,扩展性好。
3.各种状态内部消化了,不用做if判断,什么状态能干什么事,条理清晰。
4.普通的if判断考虑的是什么状态下能干什么事,比如:只有关门或停止的状态可以运行;而在状态模式下,是考虑当前状态下可以干什么事,比如关闭的状态可以开门、运行、停止。有种if要屡清逆向的思维,而状态模式只用顺着状态走。
总结:
1.调用的时候非常方便,也不用判断各种头痛的状态
2.在处理各种复杂的状态时,状态模式非常实用。比如一个页面有多个按钮,而每个按钮会弹出view,而弹出view需要隐藏或处理其他事件。视频播放时有开始播放、暂停、快进、停止甚至更多的状态。
3.解决问题才是王道,当你拿状态模式来解决复杂的问题,才能感觉到状态模式的真谛。
4.有任何问题欢迎留言交流。
参考书籍《设计模式之禅》。
网友评论