OC 实现状态模式

2019-08-29  本文已影响0人  某非著名程序员

状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。

状态模式的优点
● 结构清晰
避免了过多的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.有任何问题欢迎留言交流。

参考书籍《设计模式之禅》。

上一篇 下一篇

猜你喜欢

热点阅读