In my program I have something close to 50 events that are thrown out. The issue I’m having is settling on a standard way to recieve the events.
My first thought was to use a generic interface and just have listeners implement this and add themselves to the queue of listeners. Unfortunately this meant that classes couldn’t have multiple listeners due to type erasure and multiple inheritance issues. So I removed the listener code and started over again.
My current (and origional) setup is to have a interface that listeners for each event. Eg
/**
* 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);
}
They all extend the interface Listener which doesn’t contain any methods, its just so that they can all be collectively called “Listeners”.
The issue with this is that there is a ton of code per event. You have the event and its getters (thankfully this was simplified by Project Lombok), then an interface that receives it, then javadoc on the interface so that when someone wants information they know where to get it.
The other issue I have is that each listener has a different method name, which leads to very interesting code trying to figure out which method to call. Its fragile, slow (uses reflection), and looks like crap. If you don’t believe me, then:
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;
}
Is this though the standard way to receive events in java: Have a listener interface for each event then create spaghetti to call the appropriate methods, or am I doing this completely wrong?
At least you can get rid of the reflection/invocation logic by simplifying delegating the “event processing” to the listener. A Listener knows what type of events it can handle, so just add one method to your marker interface, like:
and change the code like this:
Now, if we have a Listener that understands the events
BreakfastandDinner, we can implement it like this (inMealListener):By the way – don’t be afraid of “tons of classes” – just find a common source pattern for all events and listeners and autogenerate the source files. In this case, you don’t have to maintain the individual event and listener source files but just the code generator and its resource file (a file based list with base names for all events and listeners)