我正在重构一个大量使用case语句和emums的现有状态机。我把它分解为Events和eventhandler对象,每个对象对应一个状态机。 eventHandlers返回新事件以通过状态机传播。
所以,我的代码看起来像这样:
public class Event {
//common fields and methods
}
public class SpecificEvent extends Event {
//fields specific to this event
}
public class AnotherEvent extends Event {
//fields specific to this event
}
public class EventHandler {
public Event handleEvent(SpecificEvent evt) {
//do default something
}
public Event handleEvent(AnotherEvent evt) {
//do default something else
}
}
public class StateOneEventHandler extends EventHandler {
public Event handleEvent(SpecificEvent evt) {
//do State1 things
}
public Event handleEvent(AnotherEvent evt) {
//do other State1 things
}
}
public class StateTwoEventHandler extends EventHandler {
public Event handleEvent(SpecificEvent evt) {
//do State2 things
}
public Event handleEvent(AnotherEvent evt) {
//do other State2 things
}
}
我的问题和疑问是:我只在我的状态机中传递通用事件引用,那么如何为事件调用正确的处理程序呢?
Event evt = new SpecificEvent();
EventHandler handler = new StateOneEventHandler();
//... later
handler.handleEvent(evt); //compiler error
完成此运行时事件“dispatch”的最佳方法是什么?
答案 0 :(得分:4)
你是正确的,重载的方法将无法解决这个问题(因为要在调用时确定要调用的方法)。
我建议你需要重构这个,要么将一个方法添加到event
中,该方法将处理程序作为参数(类似public void act(EventHandler handler)
),或者重写你的处理程序,它不需要需要强烈了解事件的类型。如果Event
是一个合理的接口/超类,它将自己暴露足够的功能,以便EventHandler不需要来了解特定的类型。
如果你真的需要,你当然可以总是施放,但一般来说你应该遵循Demeter法则并且只能通过事件界面与事件对象进行交互。
答案 1 :(得分:1)
决定调用哪条消息是在编译时完成的。因此,为了选择特定的方法变体,您需要添加一个强制转换。
您可能希望查看看似适合您的用例的访问者模式:http://en.wikipedia.org/wiki/Visitor_pattern,以获得组织类层次结构的方法,以便不需要强制转换。
另一种选择可能是将逻辑放入事件中,以便您可以使用正常的后继绑定机制。
答案 2 :(得分:0)
如何在EventHandler中使用另一种方法:
public Event handleEvent(Event evt) {
if (evt instanceof SpecificEvent) {
return handleEvent((SpecificEvent)evt);
}
if (evt instanceof AnotherEvent) {
return handleEvent((AnotherEvent)evt);
}
// code for unknown type
}
如果你有一个Event类型的变量,那么将调用它并尝试调用其他定义的方法之一。如果它是一个没有定义方法的类型扩展事件,它包含该案例的代码。在扩展EventHandler的类中,将调用覆盖的方法。
我知道它不是很漂亮,但它应该有用。
编辑:
你也可以尝试反思:
public Event handleEvent(Event evt) throws InvocationTargetException{
try {
Method m = this.getClass().getMethod("handleEvent", evt.getClass());
return (Event) m.invoke(this, evt);
} catch (NoSuchMethodException nsme) {
nsme.printStackTrace();
} catch (IllegalAccessException iae) {
iae.printStackTrace();
} catch (InvocationTargetException ite) {
ite.getCause().printStackTrace();
throw ite;
}
// code for unknown type
}
如果你有很多事件类型,这会更好。唯一的问题是,现在你必须处理例外情况。我的猜测是,如果发生NoSuchMethodException
或IllegalAccessException
,您可以忽略它们,因为这意味着没有为该事件定义任何方法(或者它无法访问),因此您需要恢复默认处理未知类型。您可能不想忽略InvocationTargetException
,因为这意味着该方法被调用但方法本身引发了异常,因此这意味着该方法中的代码存在问题。