I'm implementing a client-server system where the client is in a continuous blocking read loop listening for messages from the server. When a message is received I'd like to raise an "event" based on the type of the message, which other GUI classes may add listeners to. I'm more familiar with C# events so I am still getting used to the Java way of doing things.
There will be many message types so I will need an interface for each, call it MessageTypeAListener, MessageTypeBListener, etc., each of which will contain one handle method, which my GUI classes will implement. However, there will be be many types and instead of maintaining a list of listeners per type and having several "fire" methods I wanted to have one big listener list and a typed fire method. Then the fire method could say "only fire listeners whose type is what I specify."
So for example (pseudocode):
ListenerList.Add(MessageTypeAListener);
ListenerList.Add(MessageTypeBListener);
<T> fire(message) {
ListenerList.Where(type is T).handle(message)
}
...
fire<MessageTypeAListener>(message);
However, type erasure seems to be making this difficult. I could try casting and catching exceptions but that seems wrong. Is there a clean way of implementing this or is it just wiser to keep a separate list of listeners for every type, even though there will be tons of types?
After going through iterations of just about everyone's suggestions here, I ended up going a very slightly modified route of the standard Listener interfaces and listener lists. I started with Swing's
EventListenerList
, only to be disappointed with the amount of add/remove methods for dozens of message types. I realized this could not be condensed while still maintaining a singleEventListenerList
, so I started thinking about a separate list for each type. This makes it similar to .NET events where each event holds its own list of delegates to fire when raised. I wanted to avoid tons of add/remove methods, so I made a quickEvent
class that just looks like this:Then I keep several instances of this class around, each typed according to a listener, so
Event<MessageTypeAListener>
, etc. My classes can then call the add method to add themselves to that particular event. I would've like to be able to call a genericRaise
method on theEvent
instance to then fire all the handlers, but I did not want them to all have to have the same "handle" method, so this was not possible. Instead, when I'm ready to fire the listeners, I just doI'm sure this is not a new idea and has probably been done before and in better/more robust ways, but it's working great for me and I'm happy with it. Best of all, it's simple.
Thanks for all the help.
Old-fashioned pattern approach, using Visitor pattern:
More modern approach is just to have Map between classes of events, which should not derive each other, and respective handlers of these events. This way you avoid disadvantages of Visitor pattern (which is, you'll need to change all your visitor classes, at least, base of them, every time you add new Event).
And another way is to use Composite pattern:
I implemented something like this, cause I have a visceral dislike of Java's EventListenerList. First, you implement a generic Listener. I defined the listener based upon the Event it was receiving, with basically one method
This saves you having to define ListenerA, ListernerB etc... Though you could do it your way with ListenerA, ListenerB, etc, all extending some base like MyListener. Both ways have plusses and minuses.
I then used a
CopyOnWriteArraySet
to hold all these listeners. A set is something to consider cause all too often listeners get added twice by sloppy coders. YMMV. But, effectively you have aCollection<GenericListener<T extends Event>> or a Collection<MyListener>
Now, as you've discovered, with type erasure, the Collection can only hold one type of listener. That is often a problem. Solution: Use a Map.
Since I'm basing everything upon the event, I used
based upon the class of the event, get the list of listeners who want to get that event.
Your alternative is to base it upon the class of the listener
There's probably some typos above...
If you only have simple events, i.e. events without data or where all events have the same data types, enum could be a way forward:
Comments:
The
switch
statement in theEventListnerImpl
may be omitted if it is only registered to a single event, or if it should always act in the same way regardless of whichEvent
it receives.The
EventRegister
has stored theEventListener
(s) in a map, meaning that each listener will only get the kind ofEvent
(s) that it has subscribed to. Additionally, theEventRegister
usesSet
s, meaning that anEventListener
will only receive the event at most once (to prevent that the listener will receive two events if someone accidentally registers the listener twice).