当前位置: 首页 > 面试题库 >

基于Java枚举的状态机(FSM):传递事件

谷梁凌
2023-03-14
问题内容

我在Android应用程序中使用了几个基于枚举的状态机。尽管这些方法工作得很好,但我正在寻找一个建议,以建议如何优雅地将事件(通常是从已注册的回调或eventbus消息中)接收到当前活动状态。在许多有关基于枚举的FSM的博客和教程中,大多数都提供了消耗数据(例如解析器)的状态机示例,而不是说明如何从事件中驱动这些FSM。

我正在使用的典型状态机具有以下形式:

private State mState;

public enum State {

    SOME_STATE {


        init() {
         ... 
        }


        process() {
         ... 
        }


    },


    ANOTHER_STATE {

        init() {
         ... 
        }

        process() {
         ... 
        }

    }

}

...

在我的情况下,某些状态会触发要在特定对象上完成的工作,并注册一个侦听器。工作完成后,该对象将异步回调。换句话说,只是一个简单的回调接口。

同样,我有一个EventBus。希望收到事件通知的类再次实现了回调接口,并listen()针对EventBus上的那些事件类型进行了回调。

因此,基本问题是状态机或其单个状态,或者包含枚举FSM的类,或者 某些东西 必须实现这些回调接口,以便它们可以表示当前状态的事件。

我使用的一种方法是整体enum实现回调接口。枚举本身在底部具有回调方法的默认实现,然后各个状态可以针对它们感兴趣的事件覆盖这些回调方法。要使此状态起作用,每个状态在进入和退出时都必须注册和注销。存在非当前状态发生回调的风险。如果没有更好的选择,我可能会坚持下去。

另一种方法是让包含类实现回调。然后,它必须通过调用将这些事件委托给状态机mState.process( event )。这意味着我需要枚举事件类型。例如:

enum Events {
    SOMETHING_HAPPENED,
    ...
}

...

onSometingHappened() {

    mState.process( SOMETHING_HAPPENED );
}

但是,我不喜欢这样,因为(a)我需要在每个状态下处理switch事件类型process(event),并且(b)传递其他参数看起来很尴尬。

我想提出一个优雅的解决方案的建议,而不必诉诸使用库。


问题答案:

因此,您想将事件调度到其当前状态的处理程序。

要调度到当前状态,在每个状态变为活动状态时对其进行订阅,而在其变为非活动状态时对其进行取消订阅则非常麻烦。订阅知道活动状态的对象比较容易,只需将所有事件委托给活动状态即可。

要区分事件,可以使用单独的事件对象,然后使用visitor模式来区分它们,但这是很多样板代码。仅当我有其他代码将所有事件都视为相同时(例如,如果事件必须在交付前进行缓冲),我才会这样做。否则,我只会做类似的事情

interface StateEventListener {
    void onEventX();
    void onEventY(int x, int y);
    void onEventZ(String s);
}

enum State implements StateEventListener {
    initialState {
        @Override public void onEventX() {
            // do whatever
        }
        // same for other events
    },
    // same for other states
}

class StateMachine implements StateEventListener {
    State currentState;

    @Override public void onEventX() {
        currentState.onEventX();
    }

    @Override public void onEventY(int x, int y) {
        currentState.onEventY(x, y);
    }

    @Override public void onEventZ(String s) {
        currentState.onEventZ(s);
    }
}

编辑

如果事件类型很多,最好在运行时使用字节码工程库甚至是普通的JDK代理生成无聊的委托代码:

class StateMachine2 {
    State currentState;

    final StateEventListener stateEventPublisher = buildStateEventForwarder();

    StateEventListener buildStateEventForwarder() {
        Class<?>[] interfaces = {StateEventListener.class};
        return (StateEventListener) Proxy.newProxyInstance(getClass().getClassLoader(), interfaces, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                try {
                    return method.invoke(currentState, args);
                } catch (InvocationTargetException e) {
                    throw e.getCause();
                }
            }
        });
    }
}

这使代码的可读性较差,但确实消除了为每种事件类型编写委派代码的需要。



 类似资料:
  • 概述 FSM (有限状态机) 可以mixin到akka Actor中,其概念在Erlang 设计原则中有最好的描述。 一个 FSM 可以描述成一组具有如下形式的关系 : State(S) x Event(E) -> Actions (A), State(S') 这些关系的意思可以这样理解: 如果我们当前处于状态S,发生了E事件,则我们应执行操作A,然后将状态转换为S’。 一个简单的例子 为了演示F

  • 问题内容: 我们有一个枚举 此外,我们还有一个包含字符串的列表。有没有一种方法可以根据枚举定义的顺序(而不是自然顺序)对列表进行排序。 排序后的列表应为。 问题答案: 器具通过枚举(其中值声明的顺序)的自然顺序。如果仅通过解析创建枚举值(而不是字符串)的列表,则使用对该列表进行排序,它应该按照您想要的方式进行排序。如果您再次需要一个字符串列表,则可以通过调用每个元素来转换回来。

  • 我正在使用JacksonPolymorphicDeserialization,这是我的代码,它反序列化到基于“type”属性的适当类中: 它工作得很好,我的json根据“type”值变成了预期的类。 但是,我正在考虑将“type”属性从String移动到Enum,这是我的新代码,带有以下更改: 和枚举: 问题是,第二种方法不起作用。。。知道为什么吗???我可以在这里使用Enum吗??? 谢谢!

  • 我遇到了一些jackson多态问题。 我工作在一个web JDR字符编辑器个人项目。我使用springboot并试图坚持哲学。此外,我尝试做一些独立的包,因为学习案例为我的真正工作(另一个springboot项目)。 没有jackson配置,我没有问题序列化一个能力。但是当我试图恢复web编辑器上的任何修改时,所以当jackson对一个能力进行反序列化时,“dependance”属性就会出现问题。

  • 本文向大家介绍Linux有限状态机FSM的理解与实现,包括了Linux有限状态机FSM的理解与实现的使用技巧和注意事项,需要的朋友参考一下 有限状态机(finite state machine)简称FSM,表示有限个状态及在这些状态之间的转移和动作等行为的数学模型,在计算机领域有着广泛的应用。FSM是一种逻辑单元内部的一种高效编程方法,在服务器编程中,服务器可以根据不同状态或者消息类型进行相应的处

  • 问题内容: 如果必须在使用该对象之前对其进行初始化,那么初始化基于Java枚举的单例的正确方法是什么。 我已经开始编写代码,但是不确定是否做对了。您能帮我实现这个单例吗? 问题答案: 完全有可能为以下对象创建构造函数: 注意: 字段可以是最终的(我们喜欢) 不必是 构造函数会自动为您调用 注意最后一点。由于-singletons是在加载类时急切创建的,因此您无法将任何参数传递给构造函数。当然可以通