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



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

public enum States {
PENDING,
ACTIVE,
DONE
}

这里有多个转换+开始状态是可能的,特别是:

初始状态:PENDING or ACTIVE转换:

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

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

我还研究了其他技术,例如状态模式,但对于如此简单的问题来说,这可能有点过分了。

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

一个简单的变体是以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,则必须寻求更复杂/抽象的解决方案。

如果你正在使用 Spring,你可以考虑 Spring Statemachine。 https://projects.spring.io/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) {}
}

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

一个是上面提到的弹簧状态机,第二个是Akka有限状态机。

我的个人经验 Spring 状态机非常适合对应用程序生命周期等事物进行建模,其状态包括启动、初始化、运行、维护、错误、关闭等。但它对于建模诸如购物图表、预订、信用审批流程等内容并不是那么好......虽然它的内存占用太大,无法对数百万个实例进行建模。

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

如果你需要关于如何实现Spring State Machine的示例,你可以查看下面的博客,对于Akka FSM的例子你可以检查以下示例Blog1,Blog2。

我希望这会有所帮助。

最新更新