面向状态机编程:复杂业务逻辑应对之道

京东云开发者
• 阅读 81

作者:京东零售 樊思国

一、背景

在研发项目中,经常能遇到复杂的状态流转类的业务场景,比如游戏编程中NPC的跳跃、前进、转向等状态变化,电商领域订单的状态变化等。这类情况其实可以有一种优雅的实现方法:状态机。如下图,是操作系统对进程调度的状态机:
面向状态机编程:复杂业务逻辑应对之道
图 操作系统进程调度状态机

二、实现方式

面对以上场景,通常情况下的实现有以下几种,下面分别比较它们适用范围和优缺点:

2.1 if/else

优点:实现简单、直观。

缺点:状态多了代码可读性,业务与状态判断深度耦合,维护和扩展困难。

2.2 状态模式

状态模式类图及使用方式如下:

面向状态机编程:复杂业务逻辑应对之道

public class StatePatternDemo {
   public static void main(String[] args) {
      Context context = new Context();

      StartState startState = new StartState();
      startState.doAction(context);

      System.out.println(context.getState().toString());

      StopState stopState = new StopState();
      stopState.doAction(context);

      System.out.println(context.getState().toString());
   }
}

优点:状态单独实现,可读性比if/else好。

缺点:扩展状态需增加状态类,状态多了会出现很多状态类;并没有完全实现状态与业务解耦,不利于维护和了解整个系统状态全貌。

2.3 有限状态机

优点:严谨的数学模型,状态转移和业务逻辑基于事件完全解耦,能看到整个系统状态全貌便于维护和扩展。

缺点:需引入状态机实现方式,具备一定理解成本。

三、有限状态机

3.1 定义

有限状态机(英语:finite-state machine,缩写:FSM)又称有限状态自动机(英语:finite-state automaton,缩写:FSA),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型。

3.2 关键概念

  • 状态State:一般在状态转移图中用圆圈表示。
  • 事件Event:表示从一种状态迁移到另一种状态的触发机制。对应状态转换图中的箭头部分。
  • 动作Action: 表示状态转换后,执行的动作,但不是必须的,也可以状态转换后不执行任何动作。
  • 转移Transition:表示状态转换,从原始状态迁移到目的状态的一个过程。
  • 条件Guard:表示发生状态转移需满足的条件。

3.3 技术选型

在Java项目中,比较常用的有Spring Statemachine和Squirrel-foundation。

框架 优点 缺点
Spring Statemachine 基于Spring生态,社区强大。功能完备,支持多种状态机配置和持久化方式。 较为重量级,额外功能多。单例模式状态机不保证线程安全,只能通过工厂模式创建新的状态机实例实现,对性能有一定影响。
Squirrel-foundation 轻量级实现,状态机的创建开销小。便于二次改造,实现定制业务。 社区没有spring活跃。特殊约定较多。

综上,在下面的项目中,由于团队使用SpringBoot作为开发框架,并且项目不涉及高并发场景,故选择Spring Statemachine。

四、项目实战

在现实项目中,碰到多种状态转换的复杂业务流程,可以通过以下几个步骤进行分级,逐步将产品需求清晰的实现出来:

4.1 需求背景

零售采销在维护SKU物流属性(长、宽、高和重量)时,会因为和物流仓库侧实际属性存在不一致的情况,导致带来物流成本的偏差。为解决这个问题,需设计一个系统供采销通过操作SKU的方式,将属性下发给物流侧审核,得到最终的准确物流属性。在对SKU进行审核操作的过程中,分别存在未操作、任务下发中、下发失败、已审核、自行联系供应商和挂起6种状态(状态转换详见4.2),考虑到这些状态转换的条件分布在不同的场景下,处于对可维护性和扩展性的考虑,采用状态机实现该需求。

4.2 状态转换图

通过梳理状态转换关系,画出状态转换图如下:

面向状态机编程:复杂业务逻辑应对之道
SKU属性审核状态转换图

4.3 配置状态机

4.3.1 定义状态枚举

public enum SkuStateEnum {

    /**
     * 未操作
     */
    INIT(0, "未操作"),
    /**
     * 任务下发中
     */
    TASK_DELIVERY(1, "任务下发中"),
    /**
     * 下发失败
     */
    DELIVERY_FAIL(2, "下发失败"),
    /**
     * 复核中
     */
    RECHECKING(3, "复核中"),
    /**
     * 已复核
     */
    RECHECKED(4, "已复核"),
    /**
     * 自行联系供应商
     */
    CONCAT_SUPPLIER(5, "自行联系供应商"),
    /**
     * 挂起
     */
    SUSPEND(6, "挂起");

    /**
     * 状态代码
     */
    private Integer state;
    /**
     * 描述信息
     */
    private String desc;

    SkuStateEnum(Integer state, String desc) {
        this.state = state;
        this.desc = desc;
    }

    public static SkuStateEnum getByState(Integer state) {
        for (SkuStateEnum skuStateEnum : SkuStateEnum.values()) {
            if (skuStateEnum.getState().equals(state)) {
                return skuStateEnum;
            }
        }
        return null;
    }

    public Integer getState() {
        return state;
    }

    public String getDesc() {
        return desc;
    }
}

4.3.2 定义事件枚举

public enum SkuAttrEventEnum {
    /**
     * 调用OMC属性采集接口成功
     */
    INVOKE_OMC_ATTR_COLLECT_API_SUCCESS,
    /**
     * 调用OMC属性采集接口失败
     */
    INVOKE_OMC_ATTR_COLLECT_API_FAIL,
    /**
     * 调用OMC下发查询接口并已经生成采集单
     */
    INVOKE_OMC_SKU_DELIVERY_API_GATHER_FINISH,
    /**
     * 调用OMC下发查询接口失败
     */
    INVOKE_OMC_SKU_DELIVERY_API_FAIL,
    /**
     * OMC的MQ返回SKU属性已变更
     */
    MQ_OMC_SKU_ATTR_CHANGED,
    /**
     * 调用商品中台jsf接口,返回SKU属性已变更
     */
    INVOKE_SKU_ATTR_API_CHANGED,
    /**
     * 京东有库存
     */
    HAS_JD_STOCK,
    /**
     * 京东无库存,VMI有库存
     */
    NO_JD_STOCK_HAS_VMI_STOCK,
    /**
     * 京东和VMI均无库存
     */
    NO_JD_STOCK_NO_VMI_STOCK,
    /**
     * 上传并复核
     */
    UPLOAD_AND_RECHECK;
}

4.3.3 配置状态机

@Configuration
@EnableStateMachineFactory
@Slf4j
public class SkuAttrStateMachineConfig extends StateMachineConfigurerAdapter<SkuStateEnum, SkuAttrEventEnum> {

    /**
     * 配置状态
     *
     * @param states
     * @throws Exception
     */
    @Override
    public void configure(StateMachineStateConfigurer<SkuStateEnum, SkuAttrEventEnum> states) throws Exception {
        states.withStates().initial(SkuStateEnum.INIT)
            .states(EnumSet.allOf(SkuStateEnum.class));
    }

    @Override
    public void configure(StateMachineConfigurationConfigurer<SkuStateEnum, SkuAttrEventEnum> config) throws Exception {
        config.withConfiguration().listener(listener()).autoStartup(false);
    }

    /**
     * 配置状态转换和事件的关系
     *
     * @param transitions
     * @throws Exception
     */
    @Override
    public void configure(StateMachineTransitionConfigurer<SkuStateEnum, SkuAttrEventEnum> transitions)
        throws Exception {
        transitions.withExternal().source(SkuStateEnum.INIT).target(SkuStateEnum.TASK_DELIVERY)
            .event(SkuAttrEventEnum.INVOKE_OMC_ATTR_COLLECT_API_SUCCESS)
            .action(ctx -> {
                log.info("[调用OMC属性采集接口成功],状态变更:{} -> {}.", SkuStateEnum.INIT.getDesc(),
                    SkuStateEnum.TASK_DELIVERY.getDesc());
            })
            .and()
            .withExternal().source(SkuStateEnum.INIT).target(SkuStateEnum.DELIVERY_FAIL)
            .event(SkuAttrEventEnum.INVOKE_OMC_ATTR_COLLECT_API_FAIL)
            .action(ctx -> {
                log.info("[调用OMC属性采集接口失败],状态变更:{} -> {}.", SkuStateEnum.INIT.getDesc(),
                    SkuStateEnum.DELIVERY_FAIL.getDesc());
            })
            .and()
            .withExternal().source(SkuStateEnum.INIT).target(SkuStateEnum.CONCAT_SUPPLIER)
            .event(SkuAttrEventEnum.NO_JD_STOCK_HAS_VMI_STOCK)
            .action(ctx -> {
                log.info("[京东无库存,VMI有库存],状态变更:{} -> {}.", SkuStateEnum.INIT.getDesc(),
                    SkuStateEnum.CONCAT_SUPPLIER.getDesc());
            })
            .and()
            .withExternal().source(SkuStateEnum.INIT).target(SkuStateEnum.SUSPEND)
            .event(SkuAttrEventEnum.NO_JD_STOCK_NO_VMI_STOCK)
            .action(ctx -> {
                log.info("[京东和VMI均无库存],状态变更:{} -> {}.", SkuStateEnum.INIT.getDesc(),
                    SkuStateEnum.SUSPEND.getDesc());
            })
            .and()
            .withExternal().source(SkuStateEnum.SUSPEND).target(SkuStateEnum.TASK_DELIVERY)
            .event(SkuAttrEventEnum.HAS_JD_STOCK)
            .action(ctx -> {
                log.info("[京东有库存],状态变更:{} -> {}.", SkuStateEnum.SUSPEND.getDesc(),
                    SkuStateEnum.TASK_DELIVERY.getDesc());
            })
            .and()
            .withExternal().source(SkuStateEnum.SUSPEND).target(SkuStateEnum.RECHECKING)
            .event(SkuAttrEventEnum.INVOKE_OMC_ATTR_COLLECT_API_SUCCESS)
            .action(ctx -> {
                log.info("[调用OMC属性采集接口成功],状态变更:{} -> {}.", SkuStateEnum.SUSPEND.getDesc(),
                    SkuStateEnum.RECHECKING.getDesc());
            })
            .and()
            .withExternal().source(SkuStateEnum.SUSPEND).target(SkuStateEnum.CONCAT_SUPPLIER)
            .event(SkuAttrEventEnum.NO_JD_STOCK_HAS_VMI_STOCK)
            .action(ctx -> {
                log.info("[京东无库存,VMI有库存]:{} -> {}.", SkuStateEnum.SUSPEND.getDesc(),
                    SkuStateEnum.CONCAT_SUPPLIER.getDesc());
            })
            .and()
            .withExternal().source(SkuStateEnum.TASK_DELIVERY).target(SkuStateEnum.RECHECKING)
            .event(SkuAttrEventEnum.INVOKE_OMC_SKU_DELIVERY_API_GATHER_FINISH)
            .action(ctx -> {
                log.info("[调用OMC下发查询接口并已经生成采集单]:{} -> {}.", SkuStateEnum.TASK_DELIVERY.getDesc(),
                    SkuStateEnum.RECHECKING.getDesc());
            })
            .and()
            .withExternal().source(SkuStateEnum.TASK_DELIVERY).target(SkuStateEnum.RECHECKED)
            .event(SkuAttrEventEnum.MQ_OMC_SKU_ATTR_CHANGED)
            .action(ctx -> {
                log.info("[OMC的MQ返回SKU属性已变更]:{} -> {}.", SkuStateEnum.TASK_DELIVERY.getDesc(),
                    SkuStateEnum.RECHECKED.getDesc());
            })
            .and()
            .withExternal().source(SkuStateEnum.DELIVERY_FAIL).target(SkuStateEnum.TASK_DELIVERY)
            .event(SkuAttrEventEnum.INVOKE_OMC_ATTR_COLLECT_API_SUCCESS)
            .action(ctx -> {
                log.info("[调用OMC属性采集接口成功]:{} -> {}.", SkuStateEnum.DELIVERY_FAIL.getDesc(),
                    SkuStateEnum.TASK_DELIVERY.getDesc());
            })
            .and()
            .withExternal().source(SkuStateEnum.CONCAT_SUPPLIER).target(SkuStateEnum.RECHECKED)
            .event(SkuAttrEventEnum.INVOKE_SKU_ATTR_API_CHANGED)
            .action(ctx -> {
                log.info("[调用商品中台jsf接口,返回SKU属性已变更]:{} -> {}.", SkuStateEnum.CONCAT_SUPPLIER.getDesc(),
                    SkuStateEnum.RECHECKED.getDesc());
            });
    }

    /**
     * 全局监听器
     *
     * @return
     */
    private StateMachineListener<SkuStateEnum, SkuAttrEventEnum> listener() {
        return new StateMachineListenerAdapter<SkuStateEnum, SkuAttrEventEnum>() {
            @Override
            public void transition(Transition<SkuStateEnum, SkuAttrEventEnum> transition) {
                //当状态的转移在configure方法配置中时,会走到该方法。
                log.info("[{}]状态变更:{} -> {}", transition.getKind().name(),
                    transition.getSource() == null ? "NULL" : ofNullableState(transition.getSource().getId()),
                    transition.getTarget() == null ? "NULL" : ofNullableState(transition.getTarget().getId()));
            }

            @Override
            public void eventNotAccepted(Message<SkuAttrEventEnum> event) {
                //当发生的状态转移不在configure方法配置中时,会走到该方法,此处打印error日志,方便排查状态转移问题
                log.error("事件未收到: {}", event);
            }

            private Object ofNullableState(SkuStateEnum s) {
                return Optional.ofNullable(s)
                    .map(SkuStateEnum::getDesc)
                    .orElse(null);
            }
        };
    }
}

4.4 业务逻辑处理

4.4.1 构建状态机

对每个sku的操作,通过状态机工厂stateMachineFactory.getStateMachine

//注入状态机工厂实例
@Autowired
private StateMachineFactory<SkuStateEnum, SkuAttrEventEnum> stateMachineFactory;
//构建状态机
public StateMachine<SkuStateEnum, SkuAttrEventEnum> buildStateMachine(String skuId) throws BusinessException {
        if (StringUtils.isEmpty(skuId)) {
            return null;
        }
        StateMachine<SkuStateEnum, SkuAttrEventEnum> stateMachine = null;
        try {
            //从DB中获取当前skuId对应的状态
            LambdaQueryWrapper<SkuAttrRecheckState> query = Wrappers.lambdaQuery(SkuAttrRecheckState.class);
            query.eq(SkuAttrRecheckState::getSkuId, skuId);
            SkuAttrRecheckState skuAttrRecheckState = this.baseMapper.selectOne(query);
            SkuStateEnum skuStateEnum = SkuStateEnum.getByState(
                skuAttrRecheckState == null ? SkuStateEnum.INIT.getState() : skuAttrRecheckState.getState());
            //从状态机工厂获取一个状态机
            stateMachine = stateMachineFactory.getStateMachine(skuId);
            stateMachine.stop();
            //配置状态机参数
            stateMachine.getStateMachineAccessor().doWithAllRegions(sma -> {
                //配置状态机拦截器,当状态发生转移时,会走到该拦截器中
                sma.addStateMachineInterceptor(new StateMachineInterceptorAdapter<SkuStateEnum, SkuAttrEventEnum>() {
                    @Override
                    public void preStateChange(State<SkuStateEnum, SkuAttrEventEnum> state,
                        Message<SkuAttrEventEnum> message,
                        Transition<SkuStateEnum, SkuAttrEventEnum> transition,
                        StateMachine<SkuStateEnum, SkuAttrEventEnum> stateMachine,
                        StateMachine<SkuStateEnum, SkuAttrEventEnum> rootStateMachine) {
                        //获取状态转移时,对应的SKU详细信息
                        SkuAttrRecheckState result = JSON.parseObject(
                            String.class.cast(message.getHeaders().get(JSON_STR)), SkuAttrRecheckState.class);
                        //更新状态机转移后的状态(来自于4.3.3中的配置)
                        result.setState(state.getId().getState());
                        //将状态机转移后的状态写入DB
                        LambdaQueryWrapper<SkuAttrRecheckState> query = Wrappers.lambdaQuery(SkuAttrRecheckState.class);
                        query.eq(SkuAttrRecheckState::getSkuId, result.getSkuId());
                        if (baseMapper.exists(query)) {
                            UpdateWrapper<SkuAttrRecheckState> updateQuery = new UpdateWrapper<>();
                            updateQuery.eq("sku_id",result.getSkuId());
                            log.info("更新状态信息:{}", JSON.toJSONString(result));
                            baseMapper.update(result, updateQuery);
                        } else {
                            log.info("写入状态信息:{}", JSON.toJSONString(result));
                            baseMapper.insert(result);
                        }
                    }
                });
                //将状态机的初始状态配置为DB中的skuId对应状态
                sma.resetStateMachine(new DefaultStateMachineContext<SkuStateEnum, SkuAttrEventEnum>(
                    skuStateEnum, null, null, null));
            });
            //启动状态机
            stateMachine.start();
        } catch (Exception e) {
            log.error("skuId={},构建状态机失败.", skuId, e);
            throw new BusinessException("状态机构建失败", e);
        }
        return stateMachine;
    }

4.4.2 封装事件

public synchronized Boolean sendEvent(StateMachine<SkuStateEnum, SkuAttrEventEnum> stateMachine,
        SkuAttrEventEnum skuAttrEventEnum, SkuAttrRecheckState skuAttrRecheckState) throws BusinessException {
        try {
            //发送事件,并将需要传递的信息写入header
            stateMachine.sendEvent(MessageBuilder.withPayload(skuAttrEventEnum)
                .setHeader(SKU_ID, skuAttrRecheckState.getSkuId())
                .setHeader(STATE, skuAttrRecheckState.getState())
                .setHeader(JSON_STR, JSON.toJSONString(skuAttrRecheckState))
                .build());
        } catch (Exception e) {
            log.error("发送事件失败", e);
            throw new BusinessException("发送事件失败", e);
        }
        return true;
    }

4.4.3 业务逻辑应用

当用户在界面上对“未操作”状态的SKU点击审核按钮时,会调用物流OMC接口将SKU属性下发到物流侧,当下发成功时,状态会转换为“任务下发中”,当调用接口失败,则会将状态转换为"下发失败",核心代码如下:

public Boolean recheck(List<String> skuIds) throws BusinessException {
        if (CollectionUtils.isEmpty(skuIds)) {
            log.error("参数错误,sku列表为空");
            throw new BusinessException("参数错误,sku列表为空");
        }
        List<SkuAttrExceptionDetail> skuDetails = skuAttrExceptionDetailMapper.queryBySkuIdList(skuIds);
        if (CollectionUtils.isEmpty(skuDetails)) {
            log.error("查询sku异常明细结果集为空,skuIds={}", JSON.toJSONString(skuIds));
            return false;
        }
        for (SkuAttrExceptionDetail detail : skuDetails) {
            if (detail.getState() != SkuStateEnum.INIT.getState()) {
                log.info("{}不是未操作状态sku不进行复核", detail.getSkuId());
                continue;
            }
            //构建SKU对应的状态机
            StateMachine<SkuStateEnum, SkuAttrEventEnum> stateMachine = buildStateMachine(detail.getSkuId());
            SkuAttrRecheckState skuAttrRecheckState = DomainBuilderUtil.buildSkuAttrRecheckState(detail);
            //判定库存并发送事件
            adjustAndSendEvents(detail, stateMachine, skuAttrRecheckState);
        }
        return true;
    }

public void adjustAndSendEvents(SkuAttrExceptionDetail detail,
        StateMachine<SkuStateEnum, SkuAttrEventEnum> stateMachine,
        SkuAttrRecheckState skuAttrRecheckState) throws BusinessException {
        //1、京东有库存,调用物流属性接口,下发SKU属性
        if (detail.getSpotInventoryQtty() > 0) {
            invokeOmcSkuAttrCollectApiAndSendEvent(detail, stateMachine, skuAttrRecheckState);
            return;
        }
        //2、京东无库存,有VMI库存
        if (detail.getSpotInventoryQtty() <= 0 && detail.getVmiInventoryQtty() > 0) {
            sendEvent(stateMachine, SkuAttrEventEnum.NO_JD_STOCK_HAS_VMI_STOCK, skuAttrRecheckState);
            return;
        }
        //3、京东和VMI均无库存
        if (detail.getSpotInventoryQtty() <= 0 && detail.getVmiInventoryQtty() <= 0) {
            sendEvent(stateMachine, SkuAttrEventEnum.NO_JD_STOCK_NO_VMI_STOCK, skuAttrRecheckState);
            return;
        }
    }

private void invokeOmcSkuAttrCollectApiAndSendEvent(SkuAttrExceptionDetail detail,
        StateMachine<SkuStateEnum, SkuAttrEventEnum> stateMachine,
        SkuAttrRecheckState skuAttrRecheckState) throws BusinessException {
        DistrustAttributeGatherRequest request = RequestUtil.buildOmcAttrCollectRequest(detail, reqSource);
        try {
            if (jsfInvokeService.invokeSkuAttrCollectApi(request)) {
                sendEvent(stateMachine, SkuAttrEventEnum.INVOKE_OMC_ATTR_COLLECT_API_SUCCESS,
                    skuAttrRecheckState);
            } else {
                sendEvent(stateMachine, SkuAttrEventEnum.INVOKE_OMC_ATTR_COLLECT_API_FAIL, skuAttrRecheckState);
            }
        } catch (Exception e) {
            log.error("调用物流Sku属性采集接口错误,request={}", JSON.toJSONString(request), e);
            sendEvent(stateMachine, SkuAttrEventEnum.INVOKE_OMC_ATTR_COLLECT_API_FAIL, skuAttrRecheckState);
        }
    }

五、总结

本文通过介绍有限状态机,并结合具体项目,通过状态机的应用将状态和业务逻辑解耦,便于简化复杂业务逻辑,降低理解成本。另外,状态机的应用场景对于那种复杂的流程也同样适合,可以在实际项目中根据状态机核心要素梳理出隐性的状态转换关系,从而将一个复杂的流程问题转换为状态机的模式问题,再利用状态机模式来实现,可以有助于我们优雅的解决更广泛的复杂业务问题。

点赞
收藏
评论区
推荐文章
helloworld_28799839 helloworld_28799839
8个月前
常用知识整理
Javascript判断对象是否为空jsObject.keys(myObject).length0经常使用的三元运算我们经常遇到处理表格列状态字段如status的时候可以用到vue
京东云开发者 京东云开发者
4个月前
Squirrel状态机-从原理探究到最佳实践
Squirrel状态机是一种用来进行对象行为建模的工具,主要描述对象在它的生命周期内所经历的状态,以及如何响应来自外界的各种事件。比如订单的创建、已支付、发货、收获、取消等等状态、状态之间的控制、触发事件的监听,可以用该框架进行清晰的管理实现。使用状态机来管理对象生命流的好处更多体现在代码的可维护性、可测试性上,明确的状态条件、原子的响应动作、事件驱动迁移目标状态,对于流程复杂易变的业务场景能大大减轻维护和测试的难度。
Wesley13 Wesley13
1年前
EA&UML日拱一卒
点击上方【面向对象思考】可快速关注本公众号!行为状态机概念以下内容摘自UML2,今天的内容是说明状态机具有上下文类目的情况。行为状态机可以用于定义下面的场景(不限于)主动类的类目行为一个行为化类目的除了类目行为以外的拥有行为。如果状态机有某种行为类目
Wesley13 Wesley13
1年前
UDT协议实现分析——UDT初始化和销毁
UDT协议是一个用于在高速Internet上传输大量数据的基于UDP的可靠传输协议。我们可以将UDT协议的实现看作一个比较复杂的状态机。更准确的说,是一个主状态机,外加多个子状态机。主状态机是指协议实现中全局唯一、全局共享的状态与数据结构,主要对应于CUDTUnited类。子状态机则是对于一次UDT连接或一个Listening的UDTServer的抽象
Wesley13 Wesley13
1年前
Java生鲜电商平台
Java生鲜电商平台订单模块状态机架构设计_说明:在Java生鲜电商平台中订单的状态流转业务__我们知道一个订单会有很多种状态:临时单、已下单、待支付、待收货、待评价、已完成,退货中等等。每一种状态都和其扭转前的状态、在扭转前状态所执行的操作有关。_一实例说明举例一个过程:用户将商品加入购物车,在后台生成了一个所谓的“临
Wesley13 Wesley13
1年前
FPGA 高手养成记
来源:公众号【ZYNQ】ID  :FreeZynq整理:李肖遥本文目录1.前言2.状态机简介3.状态机分类Mealy型状态机Moore型状态机4.状态机描述一段式状态机二段式状态机三段式状态机
Wesley13 Wesley13
1年前
C语言中的状态机设计
本文不是关于软件状态机的最佳设计分解实践的教程。我将重点关注状态机代码和简单的示例,这些示例具有足够的复杂性,以便于理解特性和用法。背景大多数程序员常用的设计技术是有限状态机(FSM)。设计人员使用此编程结构将复杂的问题分解为可管理的状态和状态转换。有无数种实现状态机的方法。A switch语句提供了状态机最容易实现和最常见的版本之一。
Wesley13 Wesley13
1年前
Java设计模式之状态模式详解
(本文由言念小文原创,转载请注明出处)在实际工作中经常遇到某个对象,处于不同的状态有不同行为逻辑、且状态之间可以相互迁移的业务场景,特别是在开发通信协议栈类软件中尤为多见。《设计模式之禅》这本书中对状态模式有着非常详尽的讲解(目前为止我认为讲解得最好的书),但总觉得自己没能够理解透彻、灵活运用。直到今年完成了一个通信协议软件的开发,重新研究了“状态机”,
Wesley13 Wesley13
1年前
DSL初探之“无状态”的状态机
最近在一个项目中,因为涉及很多状态的流转,我们选择使用状态机引擎来表达状态流转。因为状态机DSL(DomainSpecificLanguages)带来的表达能力,相比较于ifelse的代码,要更优雅更容易理解。另一方面,状态机很简单,不像流程引擎那么华而不实。一开始我们选用了一个开源的状态机引擎,但我觉得不好用,就自己写了一个能满足我们要求的简洁版
Wesley13 Wesley13
1年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_