美文网首页
OC 实现状态模式

OC 实现状态模式

作者: 某非著名程序员 | 来源:发表于2019-08-29 07:56 被阅读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.有任何问题欢迎留言交流。

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

    相关文章

      网友评论

          本文标题:OC 实现状态模式

          本文链接:https://www.haomeiwen.com/subject/yezmectx.html