【问题标题】:State Machine approach for simple state transitions用于简单状态转换的状态机方法
【发布时间】:2019-12-30 20:58:42
【问题描述】:

我正在研究创建一个非常简单的状态机。我的状态机将包含以下 3 个状态:

public enum States {
    PENDING,
    ACTIVE,
    DONE
}

这里可能有多种转换+开始状态,具体来说:

初始状态:PENDING or ACTIVE 过渡:

  1. PENDING -> ACTIVE
  2. PENDING -> DONE
  3. ACTIVE -> DONE

我正在研究表示这些状态的方法以及一种可能的状态机来控制转换。我研究了一种基于枚举的方法,例如this one,但我也想向客户端公开状态转换,我不确定这在这种方法中是否合理。

我还查看了其他技术,例如State Pattern,但感觉对于这样一个简单的问题来说这可能是多余的。

对于满足此标准的简单状态机实现,是否有人有任何建议?我什至在想一些基本的东西,比如使用转换表来存储转换并在其中封装一个状态概念,该概念将使用转换表来确定下一个可能的状态。

【问题讨论】:

标签: java state state-machine state-pattern


【解决方案1】:

其中一个简单的变体是以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,您将不得不寻求更复杂/抽象的解决方案。

【讨论】:

  • 谢谢 - 这很有帮助。
【解决方案2】:

如果您使用的是 Spring,您可以考虑使用 Spring Statemachine。 https://projects.spring.io/spring-statemachine/

【讨论】:

    【解决方案3】:

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

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

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

    下面是一个非常粗糙的状态机,用于控制假设的骰子游戏。我省略了大部分细节,留下了方法的核心。请注意,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-03-27
      • 1970-01-01
      • 2020-04-07
      • 1970-01-01
      • 2015-07-28
      • 2011-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多