为了账号安全,请及时绑定邮箱和手机立即绑定

用于简单状态转换的状态机方法

用于简单状态转换的状态机方法

翻阅古今 2023-06-21 14:42:51
我正在研究创建一个非常简单的状态机。我的状态机将包含以下 3 个状态:public enum States {     PENDING,     ACTIVE,     DONE }这里可能有多个转换+起始状态,具体来说:初始状态:PENDING or ACTIVE 转换:PENDING -> ACTIVEPENDING -> DONEACTIVE -> DONE我正在研究表示这些状态的方法以及控制转换的可能状态机。我研究了一种基于枚举的方法,但我也想向客户端公开状态转换,并且我不确定这种方法是否合理。我也看过其他技术,例如State Pattern,但感觉对于这样一个简单的问题来说这可能有点矫枉过正。有人对满足此标准的简单状态机实现有任何建议吗?我什至在考虑一些基本的事情,例如使用转换表来存储转换并在其中封装一个状态概念,该状态概念将使用转换表来确定下一个可能的状态。
查看完整描述

3 回答

?
侃侃尔雅

TA贡献1801条经验 获得超15个赞

一种简单的变体是以I want to transition from X to Y while applying this function. 枚举非常适合枚举状态机中所有可能/有效的状态。我们需要一些东西来保持我们的状态转换——也许是Map<StateType, StateType>?但我们还需要某种State对象和修改它的方法 - 我们需要一个Map<StateType, Map<StateType, Transition>>. 请参阅下面的一些编译示例代码,可以帮助您入门。您可以按照自己喜欢的方式公开State对象(也许使其不可变?)并动态添加过渡。


import java.util.EnumMap;

import java.util.Map;

import java.util.function.Function;


class StackOverflowQuestion57661787 {

    enum StateType {

        PENDING,

        ACTIVE,

        DONE

    }


    //Made the name explicit here to ease readability

    public interface Transition extends Function<State, State> { }


    public static class State {

        public StateType type;

        //TODO: some real data to manipulate, or make it immutable

        public Object data;

    }


    public static class StateMachine {

        private final Map<StateType, Map<StateType, Transition>> transitions =

                new EnumMap<>(StateType.class);

        private State state;


        public StateMachine(State initialState) {

            this.state = initialState;

            for (StateType value : StateType.values()) {

                transitions.put(value, new EnumMap<>(StateType.class));

            }

        }


        public void addTransition(

                StateType input,

                StateType output,

                Transition transition

        ) {

            //TODO: handle collisions? multiple transitions for a given 

            // output statetype seems like a strange use-case

            transitions.get(input).put(output, transition);

        }


        public void moveTo(StateType toType) {

            Transition transition = transitions.get(state.type).get(toType);

            if (transition == null) {

                //TODO: handle me

                throw new RuntimeException();

            }

            //transition should modify the states "type" too OR

            //you implement it here

            state = transition.apply(state);

        }


        public State getState() {

            return state;

        }

    }

}

如果您的State对象类型依赖于当前的StateType.


查看完整回答
反对 回复 2023-06-21
?
慕工程0101907

TA贡献1887条经验 获得超5个赞

如果您使用 Spring,您可以考虑 Spring Statemachine。 

我有一个我广泛使用的个人设计,我称之为“泵”。您的状态机类有一个名为“pump”的函数,它会评估状态并相应地更新。每个状态评估可能需要来自外部源(控制器)的一些输入,例如用户或 AI。初始化状态机时需要这些对象,并且通常是抽象实现。然后,您添加应用程序可以覆盖以捕获事件的事件回调。这种方法的一个优点是“泵”方法可以从单线程或多线程系统执行。


一旦你的机器建成,你就可以通过简单地永远调用泵并提供返回随机值的控制器来轻松地进行单元测试。这实际上是一个“猴子”测试,以确保您的机器可以处理任何输入组合而不会崩溃。


然后在你的应用程序中你只需要根据情况提供合适的控制器。


下面是一个非常粗略的状态机,用于控制一个假设的骰子游戏。我省略了大部分细节,留下了方法的核心。请注意,Player.rollDice 的一种实现可能是一种等待用户按下按钮以推进游戏的阻塞方法。在这个方案中,控制游戏的所有逻辑都包含在机器中,并且可以独立于任何 UI 进行测试。


interface Player {

   boolean rollDice();

}


class Game {

   int state;

   Player [] players;

   int currentPlayer;

   int dice;


   void pump() {

      switch (state) {

         case ROLL_DICE:

            if (players[currentPlayer].rollDice()) {

               dice = Math.rand() % 6 + 1;

               onDiceRolled(dice);

               state = TAKE_TURN;

            }

            break;

         case TAKE_TURN:

            ...

            break;

      }

   }


   // base method does nothing. Users can override to handle major state transitions.

   protected void onDiceRolled(int dice) {}

}


查看完整回答
反对 回复 2023-06-21
?
UYOU

TA贡献1878条经验 获得超4个赞

我还建议您在实现自己的状态机之前检查两个框架。状态机理论对于自己开发来说真的很复杂,特别是没有太多提及的概念,如子/嵌套状态机是复杂/成功的状态机设计所必需的。

一是上面提到的 Spring 状态机,二是Akka 有限状态机。

我的个人经验 Spring State Machine 非常适合对诸如应用程序生命周期之类的事物进行建模,这些状态包括 STARTING、INITIALISING、RUNNING、MAINTENANCE、ERROR、SHUTDOWN 等……但它对于建模诸如购物图表之类的事物并不是很好、预订、信用审批流程等...但它的内存占用太大,无法对数百万个实例进行建模。

另一方面,Akka FSM 的占用空间确实很小,我个人实现了包含数百万个状态机实例的系统,并且它有另一个在 Spring 状态机中完全缺失的工具。在现代 IT 中,一件事是不可避免的,变化,您建模的任何工作流程/流程都不会随着时间的推移保持不变,因此您需要机制将这些变化集成到长期运行的工作流程/流程中(我的意思是,会发生什么如果您的流程在最新软件版本之前启动并保留旧模型,那么现在您有新版本并且模型已更改,您必须阅读保留的流程并继续使用新模型)。Akka 是针对此问题的内置解决方案Event / Schema Evolution。

查看完整回答
反对 回复 2023-06-21
  • 3 回答
  • 0 关注
  • 137 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信