目录
开发过程中的一些场景
状态模式的简单介绍
状态模式UML类图
类图讲解
适用场景
Java中的例子
案例讲解
什么是状态机
如何实现状态机
SpringBoot状态自动机
优点
缺点
与其他模式的区别
小结
-
开发过程中的一些场景
- 我们在平时的开发过程中,经常会遇到这样一种情况:就是需要我们处理一个对象的不同状态下的不同行为
- 比如最常见的就是订单,订单有很多种状态,每种状态又对应着不同的操作,有些操作是相同的,有些操作是不同的
- 再比如一个音乐播放器程序,在播放器缓冲音乐,播放,暂停,快进,快退,终止等的情况下又对应着各种操作
- 有些操作在某些情况下是允许的,有些操作是不允许的
- 还有很多不同的场景,这里就不一一列举了
-
状态模式的简单介绍
- 状态模式是行为型设计模式的一种,状态模式允许对象改变它的行为
- 其设计理念是当对象的内部状态发生改变时,随之改变其行为
- 状态和行为之间是一一对应的
- 该模式主要用于,对象的行为依赖于它的状态,并且其行为是随着状态的改变而切换时
- 这种模式接近于有限状态机的概念
- 状态模式可以被理解为策略模式,它能够通过调用在模式接口中定义的方法来切换策略
-
状态模式UML类图
-
类图讲解
- State:抽象状态接口(也可以定义成抽象类),该接口封装了所有状态所对应的行为
- ConcreteStateA/B:具体状态类,该类实现了抽象状态接口,会根据自身对应的状态来实现接口中定义的方法,还有另一个功能是指明如何过渡到下一个状态
- Context:环境(上下文)角色,该类负责状态的切换,还持有一个State实例,代表当前环境所处状态
-
适用场景
- 在以下两种情况下,请使用State模式:
- 对象的行为取决于它的状态,并且它必须在运行时根据状态更改其行为
- 根据对象状态的不同,操作有大量的条件语句;此状态通常由一个或多个枚举常量表示;通常,几个操作将包含此相同的条件结构;状态模式把条件语句的分支分别放入单独的类中;这样一来,你就可以将对象的状态视为独立的对象,该对象可以独立于其他对象而变化
- 在以下两种情况下,请使用State模式:
-
Java中的例子
- javax.faces.lifecycle.Lifecycle#execute() 方法由 FacesServlet 控制,其行为取决于当前生命周期阶段
-
案例讲解
- 场景:
- 大家的热情直接影响up的更新频率,那么此时事件和状态就出现了:
- 事件:投币,点赞,收藏
- 状态:SOMETIME(想起来什么时候更新就什么时候更新),OFTEN(会经常更新下),USUALLY(有事也更新),ALWAYS(没停过的肝)
- 我们可以得到一个关系:
- 投币:UpSometimeState -> UpOftenState
- 点赞:UpOftenState -> UpUsuallyState
- 收藏:UpUsuallyState -> UpAlwaysState
- 英文频率从低到高:Sometime -> Often -> Usually -> Always
- 大家的热情直接影响up的更新频率,那么此时事件和状态就出现了:
- 代码:
- 我们先定义一个状态的抽象类,用来表示up的更新频率
- 接着我们定义子类,分别表示每个不同的状态:
- 我们还需要一个上下文环境来进行状态的流转关联
- 接着我们写一个客户端来模拟调用流程:
- 此时,状态模式便完成了,可以看到我们没有用到if else,便完成了判断
- 每个状态也是由一个类来代替的,我们对其中一个状态进行的改动,不会影响其他的状态逻辑
- 通过这样的方式,很好的实现了对扩展开放,对修改关闭的原则
- 我们看下输出:
- 场景:
-
什么是状态机
- 定义:
- 有限状态机是一种用来进行对象行为建模的工具,其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件
- 要素:
- 现态:是指当前所处的状态
- 条件:又称为“事件”;当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移
- 动作:条件满足后执行的动作;动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态;动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态
- 次态:条件满足后要迁往的新状态;“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了
- 定义:
-
如何实现状态机
- 实现状态机主要体现在两部分:
- 画出状态转换图和将有限状态机用代码实现
- 其中代码实现部分其实就是状态模式的实现
- 画出状态转换图
- 找出所有状态(圆圈表示)
- 找出所有状态间的转换条件(圆圈间的线段表示)
- 分析每个状态需要执行的策略(圆圈边的大括号表示)
- 将有限状态机用代码实现
- 定义一个状态机类
- 根据状态转换图的状态节点(圆圈)**,定义状态**(可使用类(推荐),枚举或无符号整数)
- 根据状态转换图的转换条件(边)**,实现转换动作**方法
- 实现状态机主要体现在两部分:
-
SpringBoot状态自动机
- 完全能结合spring强大的IOC和AOP,实现一个状态自动机
- 还是刚刚的场景,我们通过Spring StateMachine来实现下
- 代码
- 包的引入:
- 定义状态和事件枚举
- 创建状态机配置类
- 注解监听器
- 创建应用Controller来完成流程
- 说明
- 我们可以对如何使用Spring StateMachine做如下小结:
- 定义状态和事件枚举
- 为状态机定义使用的所有状态以及初始状态
- 为状态机定义状态的迁移动作
- 为状态机指定监听处理器
-
优点
- 结构清晰:
- 状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”
- 将状态转换显示化,减少对象间的相互依赖:
- 将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖
- 状态类职责明确,有利于程序的扩展:
- 通过定义新的子类很容易地增加新的状态和转换
- 结构清晰:
-
缺点
- 状态模式的使用必然会增加系统的类与对象的个数
- 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱
- 状态模式对开闭原则的支持并不太好:
- 对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码
-
与其他模式的区别
- 状态模式与策略模式看起来像双胞胎,但他们还是不相同的
-
小结
- 状态模式的核心是封装,将状态以及状态转换逻辑封装到类的内部来实现,也很好的体现了“开闭原则”和“单一职责原则”
- 每一个状态都是一个子类,不管是修改还是增加状态,只需要修改或者增加一个子类即可
- 在我们的应用场景中,状态数量以及状态转换远比上述例子复杂,通过“状态模式”避免了大量的if-else代码,让我们的逻辑变得更加清晰
- 同时由于状态模式的良好的封装性以及遵循的设计原则,让我们在复杂的业务场景中,能够游刃有余地管理各个状态