大家好,欢迎来到IT知识分享网。
JAVA-stateless4j StateMachine从入门到实战
状态机第一次接触是在研究生课程《计算理论基础》上,里面有一章是讲解-有穷状态机,秦绪佳老师讲解得极其晦涩难懂。有限状态机是一种用来进行对象行为建模的工具,其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。在电商场景(订单、物流、售后)、社交(IM消息投递)、分布式集群管理(分布式计算平台任务编排)等场景都有大规模的使用。本文讲解了有限状态机的理论知识及实战演练,在实战部分,讲解了状态机在商品中心的使用,主要涉及审核相关场景:如商品、品牌、SPU在某些事件下的审核状态变更。
1、入门
1.1、为什么引入有限状态机?
最近做一个项目,项目中很多实体(Entity),每个实体都有很多状态(State),各状态会经过不同事件(Event)触发后转换到另一个状态。这些事件包括但不限于:用户页面点击触发,生效时间或失效时间到达,其他依赖实体状态变更等。在状态变更后还会有一系列动作(Action)处理。一旦相互依赖实体或实体本身状态增多,状态转换变多,处理这些状态的业务代码也会分散在各处,代码处理很容易漏掉,维护成本很高。所以考虑引入有限状态机。
1.2、什么是有限状态机?
有限状态机,也称为FSM(Finite State Machine),其在任意时刻都处于有限状态集合中的某一状态。当其获得一个输入字符时,将从当前状态转换到另一个状态,或者仍然保持在当前状态。任何一个FSM都可以用状态转换图来描述,图中的节点表示FSM中的一个状态,有向(方向表示从一个初态转换到次态)加权(权表示事件)边表示输入字符时状态的变化。如果图中不存在与当前状态与输入字符对应的有向边,则FSM将进入“消亡状态(Doom State)”,此后FSM将一直保持“消亡状态”。状态转换图中还有两个特殊状态:状态1称为“起始状态”,表示FSM的初始状态。状态6称为“结束状态”。
在启动一个FSM时,首先必须将FSM置于“起始状态”,然后触发一系列时间,最终,FSM会到达“结束状态”或者“消亡状态”。
说明:
在通常的FSM模型中,一般还存在一个“接受状态”,并且FSM可以从“接受状态”转换到另一个状态,只有在识别最后一个字符后,才会根据最终状态来决定是否接受所输入的字符串。此外,也可以将“起始状态”也作为接受状态,因此空的输入序列也是可以接受的。
TCP协议中的有限状态机
1.3、状态机要素
状态机可归纳为4个要素,即现态、条件、动作、次态。“现态”和“条件”是因,“动作”和“次态”是果。详解如下:
①现态:是指当前所处的状态。
②条件:又称为“事件”。当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
③动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
④次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。
我们可以用状态表了表示整个过程,如下图所示。
2. 有限状态机表示方法
①状态转换图,例如图1
②状态转换表,例如图2
2、实战
有限状态机实现方式
2.1. Java switch case 或者 Scala 模式匹配实现
2.2、Java枚举实现
Java枚举实现有限状态机
2.3、设计模式:状态模式实现
2.4、使用stateless4j实现
2.5、Spring Statemachine实现
2.6、Akka FSM 状态机实现
2.7、通过squirrel-foundation实现
3、在项目中的应用
3.1 状态机在商品状态的转换
业务场景:
/** * 商品审核状态枚举 */
public enum GoodsAuditStateEnum {
// 商品审核状态
NORMAL(0, "正常"),
IN_CREATE(1, "申请新增中"),
IN_UPDATE(2, "申请变更中"),
IN_DELETE(3, "申请删除中"),
IN_ON_SHELF(4, "申请上架中"),
IN_UNDER_SHELF(5, "申请下架中"),
IN_FROZEN(6, "申请冻结中"),
IN_UNFREEZE(7, "申请解冻中");
}
/** * 触发动作 */
private enum Trigger {
CREATE,
UPDATE,
DELETE,
ON_SHELF,
UNDER_SHELF,
FROZEN,
UNFREEZE,
PASS,
REJECT,
REVOKE
}
package cn.gov.zcy.service.agreement.item.fsm;
import cn.gov.zcy.service.agreement.item.enums.GoodsAuditStateEnum;
import com.googlecode.stateless4j.StateMachine;
import io.terminus.common.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
/** * 商品审核状态机 */
@Slf4j
public class GoodsAuditStateFSM {
private StateMachine<GoodsAuditStateEnum, GoodsAuditStateFSM.Trigger> stateMachine;
private GoodsAuditStateFSM(GoodsAuditStateEnum state) throws Exception {
this.stateMachine = new StateMachine<>(state);
stateMachine.Configure(GoodsAuditStateEnum.IN_CREATE).Permit(Trigger.PASS, GoodsAuditStateEnum.NORMAL).Permit(Trigger.REJECT, GoodsAuditStateEnum.NORMAL).Permit(Trigger.REVOKE, GoodsAuditStateEnum.NORMAL);
stateMachine.Configure(GoodsAuditStateEnum.IN_UPDATE).Permit(Trigger.PASS, GoodsAuditStateEnum.NORMAL).Permit(Trigger.REJECT, GoodsAuditStateEnum.NORMAL).Permit(Trigger.REVOKE, GoodsAuditStateEnum.NORMAL);
stateMachine.Configure(GoodsAuditStateEnum.IN_DELETE).Permit(Trigger.PASS, GoodsAuditStateEnum.NORMAL).Permit(Trigger.REJECT, GoodsAuditStateEnum.NORMAL).Permit(Trigger.REVOKE, GoodsAuditStateEnum.NORMAL);
stateMachine.Configure(GoodsAuditStateEnum.IN_ON_SHELF).Permit(Trigger.PASS, GoodsAuditStateEnum.NORMAL).Permit(Trigger.REJECT, GoodsAuditStateEnum.NORMAL).Permit(Trigger.REVOKE, GoodsAuditStateEnum.NORMAL);
stateMachine.Configure(GoodsAuditStateEnum.IN_UNDER_SHELF).Permit(Trigger.PASS, GoodsAuditStateEnum.NORMAL).Permit(Trigger.REJECT, GoodsAuditStateEnum.NORMAL).Permit(Trigger.REVOKE, GoodsAuditStateEnum.NORMAL);
stateMachine.Configure(GoodsAuditStateEnum.IN_FROZEN).Permit(Trigger.PASS, GoodsAuditStateEnum.NORMAL).Permit(Trigger.REJECT, GoodsAuditStateEnum.NORMAL).Permit(Trigger.REVOKE, GoodsAuditStateEnum.NORMAL);
stateMachine.Configure(GoodsAuditStateEnum.IN_UNFREEZE).Permit(Trigger.PASS, GoodsAuditStateEnum.NORMAL).Permit(Trigger.REJECT, GoodsAuditStateEnum.NORMAL).Permit(Trigger.REVOKE, GoodsAuditStateEnum.NORMAL);
stateMachine.Configure(GoodsAuditStateEnum.NORMAL)
.Permit(Trigger.CREATE, GoodsAuditStateEnum.IN_CREATE)
.Permit(Trigger.UPDATE, GoodsAuditStateEnum.IN_UPDATE)
.Permit(Trigger.DELETE, GoodsAuditStateEnum.IN_DELETE)
.Permit(Trigger.ON_SHELF, GoodsAuditStateEnum.IN_ON_SHELF)
.Permit(Trigger.UNDER_SHELF, GoodsAuditStateEnum.IN_UNDER_SHELF)
.Permit(Trigger.FROZEN, GoodsAuditStateEnum.IN_FROZEN)
.Permit(Trigger.UNFREEZE, GoodsAuditStateEnum.IN_UNFREEZE);
}
}
import com.github.oxo42.stateless4j.StateMachine;
import org.junit.Test;
/** * Created by qiwenjie on 2021/06/30. * * 状态机测试类 */
public class RunStateMachine {
private static StateMachine<CurrentState,Trigger> stateMachine = new StateMachine<CurrentState, Trigger>(CurrentState.SMALL,GoodsAuditStateFSM.config);
@Test
public void testStateMachine(){
stateMachine.Fire(Trigger.UPDATE);
System.out.println("currentState-->"+stateMachine.getState());
}
}
3.2 状态机在品牌状态的转换
业务场景:
/** * 品牌审核状态 */
public enum BrandAuditStatus {
AUDIT_FAILED(-1, "审核不通过"),
WAIT_SUBMIT(0, "待提交"),
WAIT_AUDIT(1, "待审核"),
AUDIT_PASSED(3, "审核通过");
}
/** * 品牌审核动作枚举 */
public enum BrandAuditTrigger {
/** * 提交审核 */
SUBMIT_AUDIT(0, "提交审核"),
/** * 审核通过 */
AUDIT_APPROVE(1, "审核通过"),
/** * 审核不通过 */
AUDIT_REJECT(2, "审核不通过"),
/** 审核不通过再次提交 */
SUBMIT_AUDIT_AGAIN(3, "审核不通过再次提交");
}
package cn.gov.zcy.audit.internal;
import com.googlecode.stateless4j.StateMachine;
import cn.gov.zcy.audit.enums.BrandAuditStatus;
import cn.gov.zcy.audit.enums.BrandAuditTrigger;
import lombok.extern.slf4j.Slf4j;
/** * 注册trigger */
@Slf4j
public class BrandAuditFSM {
private final StateMachine<BrandAuditStatus, BrandAuditTrigger> stateMachine;
public BrandAuditFSM(BrandAuditStatus status) {
this.stateMachine = new StateMachine<>(status);
/** * [待提交]----------[提交审核]----------[待审核] */
stateMachine.Configure(BrandAuditStatus.WAIT_SUBMIT)
.Permit(BrandAuditTrigger.SUBMIT_AUDIT, BrandAuditStatus.WAIT_AUDIT);
/** * [待审核]----------[审核通过]----------[审核通过] * * [待审核]----------[审核不通过]----------[审核不通过] */
stateMachine.Configure(BrandAuditStatus.WAIT_AUDIT)
.Permit(BrandAuditTrigger.AUDIT_APPROVE,BrandAuditStatus.AUDIT_PASSED)
.Permit(BrandAuditTrigger.AUDIT_REJECT,BrandAuditStatus.AUDIT_FAILED);
/** * [审核不通过]----------[不通过再次修改]----------[待提交] */
stateMachine.Configure(BrandAuditStatus.AUDIT_FAILED)
.Permit(BrandAuditTrigger.SUBMIT_AUDIT_AGAIN, BrandAuditStatus.WAIT_SUBMIT);
}
import com.github.oxo42.stateless4j.StateMachine;
import org.junit.Test;
/** * Created by qiwenjie on 2021/06/30. * * 状态机测试类 */
public class RunStateMachine {
private static StateMachine<CurrentState,Trigger> stateMachine = new StateMachine<CurrentState, Trigger>(CurrentState.SMALL,BrandAuditFSM.config);
@Test
public void testStateMachine(){
stateMachine.Fire(BrandAuditTrigger.AUDIT_APPROVE);
System.out.println("currentState-->"+stateMachine.getState());
}
}
3.3 状态机在SPU状态的转换
业务场景
/** * spu审核状态 */
public enum SpuAuditStatus {
AUDIT_FAILED(-1, "审核不通过"),
WAIT_SUBMIT(0, "待提交"),
WAIT_AUDIT(1, "待审核"),
AUDIT_PASSED(3, "审核通过");
}
/** * spu审核动作枚举 */
public enum SpuAuditTrigger {
/** * 提交审核 */
SUBMIT_AUDIT(0, "提交审核"),
/** * 审核通过 */
AUDIT_APPROVE(1, "审核通过"),
/** * 审核不通过 */
AUDIT_REJECT(2, "审核不通过");
}
package cn.gov.zcy.audit.internal;
import cn.gov.zcy.audit.enums.SpuAuditStatus;
import cn.gov.zcy.audit.enums.SpuAuditTrigger;
import com.googlecode.stateless4j.StateMachine;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SpuAuditFSM {
private final StateMachine<SpuAuditStatus, SpuAuditTrigger> stateMachine;
public SpuAuditFSM(SpuAuditStatus status) throws Exception {
this.stateMachine = new StateMachine<>(status);
/** * [待提交]----------[提交审核]----------[待审核] */
stateMachine.Configure(SpuAuditStatus.WAIT_SUBMIT).Permit(SpuAuditTrigger.SUBMIT_AUDIT,
SpuAuditStatus.WAIT_AUDIT);
/** * [待审核]----------[审核通过]----------[审核通过] * * [待审核]----------[审核被拒]----------[审核不通过] */
stateMachine.Configure(SpuAuditStatus.WAIT_AUDIT)
.Permit(SpuAuditTrigger.AUDIT_APPROVE, SpuAuditStatus.AUDIT_PASSED)
.Permit(SpuAuditTrigger.AUDIT_REJECT, SpuAuditStatus.AUDIT_FAILED);
}
}
import com.github.oxo42.stateless4j.StateMachine;
import org.junit.Test;
/** * Created by qiwenjie on 2021/06/30. * * 状态机测试类 */
public class RunStateMachine {
private static StateMachine<CurrentState,Trigger> stateMachine = new StateMachine<CurrentState, Trigger>(CurrentState.SMALL,SpuAuditFSM.config);
@Test
public void testStateMachine(){
stateMachine.Fire(SpuAuditTrigger.AUDIT_APPROVE);
System.out.println("currentState-->"+stateMachine.getState());
}
}
自律是解决人生问题最主要的工具,也是消除人生痛苦最重要的方法
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/24287.html