freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Squirrel状态机-从原理探究到最佳实践
2023-01-28 11:42:03
所属地 北京

作者:京东物流 郑朋辉

1 简介

Squirrel状态机是一种用来进行对象行为建模的工具,主要描述对象在它的生命周期内所经历的状态,以及如何响应来自外界的各种事件。比如订单的创建、已支付、发货、收获、取消等等状态、状态之间的控制、触发事件的监听,可以用该框架进行清晰的管理实现。使用状态机来管理对象生命流的好处更多体现在代码的可维护性、可测试性上,明确的状态条件、原子的响应动作、事件驱动迁移目标状态,对于流程复杂易变的业务场景能大大减轻维护和测试的难度。

2 基本概念

2.1 Squirrel状态机定义

Squirrel状态机是一种有限状态机,有限状态机是指对象有一个明确并且复杂的生命流(一般而言三个以上状态),并且在状态变迁存在不同的触发条件以及处理行为。

2.2 Squirrel状态机要素

Squirrel状态机可归纳为4个要素,即现态、条件、动作、次态。“现态”和“条件”是因,“动作”和“次态”是果。

  • 现态:是指当前所处的状态。
  • 条件:又称为事件。当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
  • 动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
  • 次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

3 实现原理

3.1 店铺审核CASE

举例,京东线上开店需要经过审核才能正式上线,店铺状态有待审核、已驳回、已审核,对应操作有提交审核,审核通过,审核驳回动作。现在需要实现一个店铺审核流程的需求。

3.2 方案对比

3.2.1 常用if-else或switch-case实现(分支模式)

1674878187_63d49ceb46d2bf9a7a341.png!small?1674878187854

图1.if-else/switch-case模式实现流程图

3.2.2 状态机实现

1674878198_63d49cf6945fe60a0a60e.png!small?1674878199044

图2.状态机模式实现流程图

3.2.3 对比

通过引入状态机,可以去除大量if-else if-else或者switch-case分支结构,直接通过当前状态和状态驱动表查询行为驱动表,找到具体行为执行操作,有利于代码的维护和扩展。

3.3 实现原理

1674878210_63d49d02513cdc8b984b7.png!small?1674878212052

图3.状态机创建流程图

  • StateMachine: StateMachine实例由StateMachineBuilder创建不被共享,对于使用annotation方式(或fluent api)定义的StateMachine,StateMachine实例即根据此定义创建,相应的action也由本实例执行,与spring的集成最终要的就是讲spring的bean实例注入给由builder创建的状态机实例;
  • StateMachineBuilder: 本质上是由StateMachineBuilderFactory创建的动态代理。被代理的StateMachineBuilder默认实现为StateMachineBuilderImpl,内部描述了状态机实例创建细节包括State、Event、Context类型信息、constructor等,同时也包含了StateMachine的一些全局共享资源包括StateConverter、EventConverter、MvelScriptManager等。StateMachineBuilder可被复用,使用中可被实现为singleton;
  • StateMachineBuilderFactory: 为StateMachineBuilder创建的动态代理实例;

4 实践分享

4.1 环境依赖

<dependency>
    <groupId>org.squirrelframework</groupId>
    <artifactId>squirrel-foundation</artifactId>
    <version>0.3.9</version>
</dependency>

4.2 状态机元素定义:状态、事件

// 店铺审核状态
public Enum ShopInfoAuditStatusEnum{
audit(0,"待审核"),
agree(1,"审核通过"),
reject(2,"审核驳回");
}
// 店铺审核事件
public Enum ShopInfoAuditEvent{
SUBMIT, // 提交
AGREE,  // 同意
REJECT; // 驳回
}

4.3 构建StateMachineBuilder实例

/**
 * StateMachineBuilder实例
 */
public class StateMachineEngine <T extends UntypedStateMachine, S, E, C> implements ApplicationContextAware{


    private ApplicationContext applicationContext;


    private static Map<String,UntypedStateMachineBuilder> builderMap = new HashMap<String,UntypedStateMachineBuilder>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


    @Transactional
    public void fire(Class<T> machine, S state, E event, C context) {
        StateMachineBuilder stateMachineBuilder = this.getStateMachineBuilder(machine);
        StateMachine stateMachine = stateMachineBuilder.newStateMachine(state,applicationContext);
        stateMachine.fire(event, context);
     }


     private StateMachineBuilder getStateMachineBuilder(Class<T> stateMachine){
         UntypedStateMachineBuilder stateMachineBuilder = builderMap.get(stateMachine.getName());
         if(stateMachineBuilder == null){
             stateMachineBuilder = StateMachineBuilderFactory.create(stateMachine,ApplicationContext.class);
             builderMap.put(stateMachine.getName(),stateMachineBuilder);
         }
         return stateMachineBuilder;

4.4 创建具体店铺状态审核状态机

/**
 * 店铺审核状态机
 */
@States({
    @State(name = "audit"),
    @State(name = "agree"),
    @State(name = "reject")
})
@Transitions({
    @Transit(from = "audit", to = "agree", on = "AGREE", callMethod = "agree"),
    @Transit(from = "audit", to = "reject", on = "REJECT", callMethod = "reject"),
    @Transit(from = "reject", to = "audit", on = "SUBMIT", callMethod = "submit"),
    @Transit(from = "agree", to = "audit", on = "SUBMIT", callMethod = "submit"),
    @Transit(from = "audit", to = "audit", on = "SUBMIT", callMethod = "submit"),
})
@StateMachineParameters(stateType=ShopInfoAuditStatusEnum.class, eventType=ShopInfoAuditEvent.class, contextType=ShopInfoAuditStatusUpdateParam.class)
public class ShopInfoAuditStateMachine extends AbstractUntypedStateMachine {


    private ApplicationContext applicationContext;


    public ShopInfoAuditStateMachine(){}


    public ShopInfoAuditStateMachine(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }


    // 审核通过业务逻辑
    public void agree(ShopInfoAuditStatusEnum fromState, ShopInfoAuditStatusEnum toState, ShopInfoAuditEvent event, ShopInfoAuditStatusUpdateParam param) {
        this.agree(fromState,toState,event,param);
    }

    // 审核驳回业务逻辑
    public void reject(ShopInfoAuditStatusEnum fromState, ShopInfoAuditStatusEnum toState, ShopInfoAuditEvent event, ShopInfoAuditStatusUpdateParam param) {
        this.reject(fromState,toState,event,param);
    }

    // 提交业务逻辑
    public void submit(ShopInfoAuditStatusEnum fromState, ShopInfoAuditStatusEnum toState, ShopInfoAuditEvent event, ShopInfoAuditStatusUpdateParam param) {
        this.submit(fromState,toState,event,param);
    }

4.5 客户端调用

// 调用端
main{
StateMachineEngine stateMachineEngine = applicationContext.getBean(StateMachineEngine.class);
// 审核通过调case
stateMachineEngine.fire(ShopInfoAuditStateMachine.class,ShopInfoAuditStatusEnum.audit,ShopInfoAuditEvent.AGREE,param);
// 审核驳回case
stateMachineEngine.fire(ShopInfoAuditStateMachine.class,ShopInfoAuditStatusEnum.audit,ShopInfoAuditEvent.REJECT,param);
}

5 总结

状态机很好的帮我们处理了对象状态的流转、事件的监听以及外界的各种事件的响应。从代码设计角度减少了大量if-else/switch-case逻辑判断,提高了代码的可维护性、扩展性,方便管理和测试。

# 系统安全 # 数据安全 # 安全测试 # 数据建模
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者