基于Java枚举的状态机(FSM):传递事件 [英] Java enum-based state machine (FSM): Passing in events

查看:210
本文介绍了基于Java枚举的状态机(FSM):传递事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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


$ b $我使用的典型状态机有这样的形式:

  private state mState; 

public enum State {

SOME_STATE {


init(){
...
}


process(){
...
}


},


ANOTHER_STATE {

init(){
...
}

进程(){
...
}

}

}

...

在我的情况下,一些状态触发了一件工作要在特定对象上完成,注册一个监听器。当工作完成时,该对象异步回调。换句话说,只是一个简单的回调界面。



同样,我有一个EventBus。希望通知事件的类再次为EventBus上的事件类型实现一个回调接口和 listen()



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



我使用的一种方法是整个枚举来实现回调接口。枚举本身具有底部的回调方法的默认实现,然后各个状态可以覆盖他们感兴趣的事件的回调方法。为了这个工作,每个状态必须在进入和退出时注册和注销,否则在不是当前状态的状态下存在回调的风险。我可能会坚持这个,如果我没有找到更好的东西。



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

 枚举事件{
SOMETHING_HAPPENED,
...
}

...

onSometingHappened(){

mState.process(SOMETHING_HAPPENED);
}

我不喜欢这个,因为(a)我会每个州的进程(事件)内的事件类型需要切换的丑陋,(b)传递通过附加参数看起来很尴尬。



我想要一个优雅的解决方案,而不使用图书馆。

解决方案

所以你想要将事件发送到他们当前状态的处理程序。



要调度到当前状态,订阅每个状态变为活动状态,并在它变为非活动状态时将其取消订阅是相当麻烦的。订阅一个知道活动状态的对象是更容易的,只需将所有事件委托给活动状态。



为了区分事件,可以使用单独的事件对象,然后将它们与访问者模式进行区分,但这是相当多的样板代码。我只会这样做,如果我有其他代码来处理所有的事件是相同的(例如,如果事件必须在交付前缓冲)。否则,我只会做一些类似

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

枚举状态实现StateEventListener {
initialState {
@Override public void onEventX(){
// do whatever
}
//其他事件相同
},
//其他状态相同
}

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(){
类<?> [] 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();
}
}
});
}
}

这使得代码可读性降低,需要为每个事件类型写代码代码。


I'm using several enum-based state machines in my Android application. While these work very well, what I am looking for is a suggestion for how to elegantly receive events, typically from registered callbacks or from eventbus messages, into the currently active state. Of the many blogs and tutorials concerning enum-based FSMs, most of them give examples of state machines that consume data (e.g. parsers) rather than show how these FSMs may be driven from events.

A typical state machine I'm using has this form:

private State mState;

public enum State {

    SOME_STATE {


        init() {
         ... 
        }


        process() {
         ... 
        }


    },


    ANOTHER_STATE {

        init() {
         ... 
        }

        process() {
         ... 
        }

    }

}

...

In my situation, some of the states trigger a piece of work to be done on a particular object, registering a listener. That object asynchronously calls back when the work is done. In other words, just a simple callback interface.

Similarly, I have an EventBus. Classes wanting to be notified of events again implement a callback interface and listen() for those event types on the EventBus.

The basic problem therefore is that the state machine, or its individual states, or the class containing the enum FSM, or something has to implement those callback interfaces, so that they can represent events on the current state.

One approach I have used is for the entire enum to implement the callback interface(s). The enum itself has default implementations of the callback methods at the bottom, and the individual states can then override those callback methods for events they're interested in. For this to work, each state must register and unregister as it enters and exits, otherwise there is risk of the callback happening on a state that isn't the current state. I will probably stick with this if I find nothing better.

Another way is for the containing class to implement the callbacks. It then has to delegate those events on to the state machine, by calling mState.process( event ). That means I'd need to enumerate event types. For example:

enum Events {
    SOMETHING_HAPPENED,
    ...
}

...

onSometingHappened() {

    mState.process( SOMETHING_HAPPENED );
}

I don't like this however because (a) I'd have the uglyness of needing to switch on the event types within the process(event) of each state, and (b) passing through additional parameters looks awkward.

I would like a suggestion for an elegant solution for this without resorting to using a library.

解决方案

So you want to dispatch events to their handlers for the current state.

To dispatch to the current state, subscribing each state as it becomes active, and unsubscribing it as it becomes inactive is rather cumbersome. It is easier to subscribe an object that knows the active state, and simply delegates all events to the active state.

To distinguish events, you can use separate event objects, and then distinguish them with the visitor pattern, but that's quite a bit of boilerplate code. I'd only do this if I have other code that treats all events the same (for instance, if events must be buffered before delivery). Otherwise, I'd simply do something like

interface StateEventListener {
    void onEventX();
    void onEventY(int x, int y);
    void onEventA(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);
    }
}

Edit

If you have many event types, it might be better to generate the boring delegation code at runtime using a bytecode engineering library, or even a plain JDK proxy:

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();
                }
            }
        });
    }
}

This makes the code less readable, but does eliminate the need to write delegation code for each event type.

这篇关于基于Java枚举的状态机(FSM):传递事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆