基于状态机(FSM):传入事件
我在我的Android应用程序中使用了几个基于枚举的状态机。 虽然这些工作非常好,但我正在寻找的是关于如何优雅地接收事件(通常是从注册回调或事件总线消息)到当前活动状态的建议。 在许多关于基于枚举的FSM的博客和教程中,大多数博客和教程都提供了使用数据的状态机(例如解析器)的示例,而不是展示如何根据事件驱动这些FSM。
我使用的典型状态机有这种形式:
private State mState;
public enum State {
SOME_STATE {
init() {
...
}
process() {
...
}
},
ANOTHER_STATE {
init() {
...
}
process() {
...
}
}
}
...
在我的情况下,一些国家触发了一项工作要完成一个特定的对象,注册一个监听器。 该对象在工作完成时异步回调。 换句话说,只是一个简单的回调接口。
同样,我有一个EventBus。 想要再次通知事件的类实现一个回调接口,并在EventBus上为这些事件类型listen()
。
因此,基本的问题是状态机或其各个状态或包含枚举FSM的类或某些事情必须实现这些回调接口,以便它们可以表示当前状态的事件。
我使用的一种方法是让整个enum
实现回调接口。 枚举本身具有底部回调方法的默认实现,然后各个状态可以覆盖那些他们感兴趣的事件的回调方法。为此,每个状态必须在进入和退出时注册和取消注册,否则在不是当前状态的状态下有发生回调的风险。 如果我找不到更好的东西,我可能会坚持下去。
另一种方法是让包含类实现回调。 然后它必须通过调用mState.process( event )
将这些事件委托给状态机。 这意味着我需要枚举事件类型。 例如:
enum Events {
SOMETHING_HAPPENED,
...
}
...
onSometingHappened() {
mState.process( SOMETHING_HAPPENED );
}
然而,我不喜欢这样做,因为(a)我需要在每个状态的process(event)
内switch
事件类型的丑陋,并且(b)通过其他参数看起来很尴尬。
我想建议一个优雅的解决方案,而不诉诸使用图书馆。
所以你想把事件分派给它们的当前状态的处理程序。
为了分派到当前状态,订阅每个状态为活动状态,并且在它变为非活动状态时取消订阅它相当麻烦。 订阅知道活动状态的对象并将所有事件委托给活动状态会更容易。
为了区分事件,您可以使用单独的事件对象,然后将它们与访问者模式区分开来,但这是相当多的样板代码。 如果我有其他处理所有事件的代码(例如,事件必须在交付之前进行缓冲),我才会这样做。 否则,我只会做类似的事情
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);
}
}
编辑
如果您有很多事件类型,最好在运行时使用字节码工程库或甚至普通的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();
}
}
});
}
}
这会使代码不易读,但不需要为每个事件类型编写委派代码。
为什么不直接在状态中调用正确的回调?
public enum State {
abstract State processFoo();
abstract State processBar();
State processBat() { return this; } // A default implementation, so that states that do not use this event do not have to implement it anyway.
...
State1 {
State processFoo() { return State2; }
...
},
State2 {
State processFoo() { return State1; }
...
}
}
public enum Event {
abstract State dispatch(State state);
Foo {
State dispatch(State s) { return s.processFoo(); }
},
Bar {
State dispatch(State s) { return s.processBar(); }
}
...
}
这可以解决您的两种预订方式:无“丑陋”开关,并且没有“尴尬”的额外参数。
你有良好的轨迹,你应该使用与状态机相结合的策略模式。 在状态枚举中实现事件处理,提供默认的通用实现,并可能添加特定的实现。
定义您的事件和相关策略界面:
enum Event
{
EVENT_X,
EVENT_Y,
EVENT_Z;
// Other events...
}
interface EventStrategy
{
public void onEventX();
public void onEventY();
public void onEventZ();
// Other events...
}
然后,在你的State
枚举中:
enum State implements EventStrategy
{
STATE_A
{
@Override
public void onEventX()
{
System.out.println("[STATE_A] Specific implementation for event X");
}
},
STATE_B
{
@Override
public void onEventY()
{
System.out.println("[STATE_B] Default implementation for event Y");
}
public void onEventZ()
{
System.out.println("[STATE_B] Default implementation for event Z");
}
};
// Other states...
public void process(Event e)
{
try
{
// Google Guava is used here
Method listener = this.getClass().getMethod("on" + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, e.name()));
listener.invoke(this);
}
catch (Exception ex)
{
// Missing event handling or something went wrong
throw new IllegalArgumentException("The event " + e.name() + " is not handled in the state machine", ex);
}
}
// Default implementations
public void onEventX()
{
System.out.println("Default implementation for event X");
}
public void onEventY()
{
System.out.println("Default implementation for event Y");
}
public void onEventZ()
{
System.out.println("Default implementation for event Z");
}
}
根据EventStrategy
,所有事件都有一个默认实现。 而且,对于每个状态而言,针对不同事件处理的具体实现是可能的。
StateMachine
看起来像这样:
class StateMachine
{
// Active state
State mState;
// All the code about state change
public void onEvent(Event e)
{
mState.process(e);
}
}
在这种情况下,您信任mState是当前活动状态,所有事件仅应用于此状态。 如果你想添加一个安全层,为所有非活动状态禁用所有事件,你可以做到这一点,但在我看来,这不是一个好的模式,它不是一个State
知道它是否活跃,而是StateMachine
工作。
上一篇: based state machine (FSM): Passing in events
下一篇: c++