在我的程序中,有近50个被抛出的事件。我遇到的问题是采用标准的方式接收事件。
我的第一个想法是使用通用接口,只是让侦听器实现它并将自己添加到侦听器队列中。 Unfortunately this meant that classes couldn't have multiple listeners due to type erasure and multiple inheritance issues。所以我删除了侦听器代码并重新开始。
我当前(和原创)的设置是为每个事件提供一个侦听器接口。例如
/**
* Listener for {@link myproject.hooks.events.FingerEvent}events
* @see myproject.hooks.events.Finger
*/
public interface FingerListener extends Listener {
/**
* Invoked when an {@link myproject.hooks.events.FingerEvent}occurs
* @param event The generated FingerEvent
*/
public void onFinger(FingerEvent event);
}
它们都扩展了不包含任何方法的接口Listener
,只是为了将它们全部统称为“监听器”。
这个问题是每个事件都有大量的代码。你有事件及其获取者(幸运的是,这是由Project Lombok简化的),然后是接收它的接口,然后接口上的javadoc,以便当有人想要信息时,他们知道从哪里获取信息。
我遇到的另一个问题是每个侦听器都有一个不同的方法名称,这会导致非常有趣的代码试图找出要调用的方法。它脆弱,缓慢(使用反射),看起来像垃圾。如果你不相信我,那么:
public static boolean callListener(Event event, Listener listener) {
//Get base name of event
String name = event.getClass().getSimpleName().split("Event")[0];
//Try and get the correct method if it exists
Method listenerMethod = null;
try {
listenerMethod = listener.getClass().getMethod("on"+name, event.getClass());
} catch (NoSuchMethodException ex) {
//Method doesn't exist, just don't call anything
return false;
} catch (SecurityException ex) {
throw new RuntimeException("Method on"+name+" is unaccessable", ex);
}
//Now that we have the method, attempt to execute it
try {
listenerMethod.invoke(listener, event);
} catch (Exception ex) {
throw new RuntimeException("Unexpected error when invoking method on"+name);
}
//Method executed sucessfully, return true
return true;
}
这是否是在java中接收事件的标准方法:为每个事件设置一个监听器接口,然后创建spaghetti来调用相应的方法,或者我这样做完全错了吗?
答案 0 :(得分:2)
至少可以通过简化向侦听器委派“事件处理”来摆脱反射/调用逻辑。监听器知道它可以处理的事件类型,因此只需在标记界面中添加一个方法,例如:
public interface Listener {
public boolean process(Event event);
}
并更改代码如下:
public static boolean callListener(Event event, Listener listener) {
return listener.process(event);
}
现在,如果我们有一个了解事件Breakfast
和Dinner
的监听器,我们可以像这样实现它(在MealListener
中):
public class MealListener implements Listener {
@Override
public boolean process(Event event) {
if (event instanceof Breakfast) {
this.onBreakfast((Breakfast) event);
return true;
}
if (event instanceof Dinner) {
this.onDinner((Dinner) event);
return true;
}
return false; // MealListener ignores this event
}
private void onBreakfast(Breakfast breakfastCall) {
// eat breakfast
}
private void onDinner(Dinner dinnerCall) {
// eat dinner
}
}
顺便说一句 - 不要害怕“大量的课程” - 只需为所有事件和听众找到一个共同的源模式并自动生成源文件。在这种情况下,您不必维护单个事件和侦听器源文件,而只需维护代码生成器及其资源文件(基于文件的列表,其中包含所有事件和侦听器的基本名称)
答案 1 :(得分:0)
你的设计错了。无论什么类触发事件,都应该调用接口中定义的方法。
所以你的界面应该是这样的:
public interface FingerListener { public void listenerCallback(FingerEvent event); }
然后您可以简单地遍历您的侦听器并为每个已注册的侦听器调用listenerCallback
方法。
如果接收者需要区分不同类型的事件,我会向FingerEvent类添加一个“类型代码”属性,可以由接收者检查。