状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作、完成特定操作的控制中心。以上是百度百科对状态机的解释。
在百科的解释中,我们可以提炼出状态机的几个要素:存储状态,逻辑电路,预先设定的状态转移路径,外部来的信号,内置的特定操作等。由这些关键要素我们可以推断出以下几点:
-
状态数据有预先设定的多种值
-
逻辑电路是完成内置特定操作的基础“代码“(基础设施),由工程司开发
-
状态能在多种值之间转移
-
每次转移都由外部信号触发
-
状态转移触发后,会有相应的内置操作
由以上这些特性共同构成了一个独立的控制中心,而且这个控制中心与外部各种信号是低耦合的,所有外部信号都要接入一个共同的控制中心,最终也由控制中心完成信号的后续流程。
在java开发中,状态机FSM(有限状态机)是一种常见的设计模式,常常用于一些复杂的业务场景,解决繁琐杂乱的if...else...代码段。
“Allow an object to alter its behavior when its internal state changes.The object will appear to change its class.“
-对象内部状态不同会有不同的行为。似乎好像是不同的类一样。
以电梯为例,电梯有4种状态:开门、关门、运行、停止。电梯的动作:开门、关门、运行(上升或下降)、停止(悬停不动)。
Col1 | 开门行为 | 关门行为 | 运行行为 | 停止行为 |
---|---|---|---|---|
开门 态 | ❌ | ✔ | ❌ | ❌ |
关门 态 | ✔ | ❌ | ✔ | ✔ |
运行 态 | ❌ | ❌ | ❌ | ✔ |
停止 态 | ✔ | ❌ | ✔ | ❌ |
-
开门状态下,电梯只有关门行为
-
关门状态下,除了关门行为其他行为都有
-
运行状态下,只有停止行为
-
停止状态下,有开门行为和运行行为
状态图如下:
状态机设计模式的好处:
-
降低程序的复杂度;
-
提高程序的可维护性;
-
状态机模式体现了开闭原则和单一职责原则。
-
每个状态都是一个子类,增加状态就要增加子类;修改状态只要修改一个类就行了。
这种设计模式将每个状态的变更后的处理逻辑都做了统一封装,跟业务代码耦合,只接收相互约定的信号(事件)。
由状态机的几个要素我们也可以知道,在一个复杂的业务流程过程中,有多种数据状态,多种处理动作,以及多种维度的角色。在这样复杂业务场景下,如果只是简单使用if...else...语句,首先可读性就非常差,而且维护起来非常困难,甚至是开发人员都没办法理清楚自己写的if..else语句。如果复杂的业务流程有了变更和新增,那这个维护起来就是个灾难,各种复杂问题不得而知。就像下图一样
用if..else..代码来开发复杂的业务流程,会面临下面几个问题:
-
复杂的业务流程,if.else代码几乎无法维护
-
随着业务的发展,业务过程也需要变更及扩展,但if.else代码段已经无法支持
-
没有可读性,变更风险特别大,可能会牵一发而动全身,给服务带来S级风险。
-
其他业务逻辑可能也会跟if.else代码块耦合在一起,带来更多的问题。
这时候,状态机就是个非常好的解决方案,能有效地解决这些问题。
一个状态机定义以后,在某个状态下就只接收固定的Event,也就是执行指定的操作,这样流程就能按照预期定义的那样流转,不会出现乱入的情况,执行了一些在某状态下不允许执行的操作,也就是说,状态的流转都是在控制范围,不会超出预设的状态空间。
-
状态机建立的控制中心是跟外界低耦合的,通过event通信。
-
控制中心所有的状态都是预设好的,不会超预料。
-
状态的跳转都是有设定控制条件的,会按照预设的转移路径运动。
-
状态机还非常容易的扩展和变更,支持因业务的发展而变更或扩展复杂业务流程
状态机典型的应用有工作流引擎,游戏中人物状态变化引擎,订单交易等。前两种非常复杂,下面我就简单以订单交易这个场景来聊聊状态机的应用场景以及基于spring statemachine的项目。
配置pom依赖:
<dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-core</artifactId> <version>2.1.2.RELEASE</version> </dependency>
状态枚举和事件枚举:
/** * @author Batman create on 2019-05-07 14:59 * 状态枚举类 */ public enum States { /* 待支付 */ UNPAID, /* 待收货 */ WAITING_FOR_RECEIVE, /* 结束 */ DONE, /* 退货中 */ WAITING_FOR_GOODSBACK } /** * @author Batman create on 2019-05-07 15:00 */ public enum Events { /* 支付 */ PAY, /* 收货 */ RECEIVE, /* 退货 */ GOODS_REJECTED, /* 退款 */ REFUND }
查看更多 “Java架构师方案” 系列文章 以及 SpringBoot2.0学习示例
如果大家觉得这篇文章对你学习架构有帮助的话,还请点赞,在看支持一下。github项目也记得点个星哦!
还要配置状态转移信息以及事件处理器逻辑的开发。完整的demo项目,请关注公众号“前沿科技bot“并发送"状态机"获取。