美文网首页
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 实现状态模式

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

  • iOS如何实现多代理模式--OC

    OC 如何实现多代理模式 为什么要使用多代理模式 标题虽然是如何实现多代理模式,但是知道为什么需要实现多代理模式同...

  • 状态模式

    状态模式定义: Swift、OC开发中,状态模式不多,服务器开发里面、Android移动端系统源码有的->Wifi...

  • 状态模式实现

    图示 输出 代码 main 状态机 状态的抽象接口 A状态 B状态 C状态

  • iOS web交互

    iOS极简模式实现Webview网页图片原生预览 IOS中 使用JavaScriptCore 实现OC与JS的交互...

  • Objective-C有多继承吗?没有的话用什么代替?

    OC没有多继承,通过设计模式实现多继承1.通过协议(protocol)实现,协议只能提供接口,不能提供实现方法缺点...

  • iOS KVO的底层实现原理

    KVO 是 OC 观察者设计模式的一种KVO 的实现依赖于 OC 强大的 RuntimeKVO是Cocoa提供的...

  • 用OC代码认识设计模式(一)--创建型模式

    参考:iOS设计模式四部曲(一):创建型模式 内附Demo大话设计模式之oc实现23种模式风中独思--设计模式<简...

  • 设计模式---StateMode

    一、什么是StateMode(状态模式) 状态模式与上一篇写的《设计模式---Strategy模式》具体实现结构有...

  • 用OC认识设计模式(二)--结构型模式

    用OC代码认识设计模式(一)--创建型模式用OC代码认识设计模式(二)--结构型模式用OC代码认识设计模式(三)-...

网友评论

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

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