Suppose you have two abstractions – an event listener and an event producer. The relationship is 1:1, that is, there’s one abstraction that fires events, and another one that knows how to deal with them. At some later time it turns out there should be more than one listener. If you’re coding in Java, your programming language goes into your way.
Here is what I mean:
class CarMaker{ CarMakerListener listener; public void doStuff(){ listener.productionStartsUp(); .... listener.productionIsGoingWell(); .... listener.productionIsShuttingDown(); .... listener.productionDown(); } }
Everything is simple and beautiful – the listener is whatever you need it to be. It can be a mock object, a logger, UI element, a NullObject. Whatever.
But now it turns out you need both a logger and an UI element. And probably a performance monitor later on. A natural way to do this is by putting your listeners to the list and calling the method in loop:
// instead of listener.methodXXX() for(IListener listener: listeners) listener.methodXXX();
My problem with such solution is that it requires me to write for-loops all over the place. It’s a boilerplate code repeated all over. Which doesn’t seem elegant.
For functional programming languages, there should be no problems. Even the .NET world has LINQ and delegates.
But Java explicitly doesn’t. Stack overflow hints there are NO way for Java to have delegates. Sun/Oracle suggests using anonymous inner classes instead of delegates which in such cases is unacceptable.
Any suggestions about a way out?
A hand-made solution: the generic ListenerList object based on good old Proxy . The above code would look like this:
class CarMaker{ ListenerList<ICarMakerListener> listeners; public void doStuff(){ listeners.all().productionStartsUp(); // calls productionStartsUp() for every listener registered .... listeners.all().productionIsGoingWell(); // etc .... listeners.all().productionIsShuttingDown(); // etc .... listeners.all().productionDown(); // etc ... listeners.all().methodXYZ(); // calls methodXYZ for every listener registered } }
And this is Java 1.5 compatible, which is good for old code maintenance.
The source code of my hand-made ListenerList is hereby generously released under GLP v3. Behold!
public class ListenerList <ListenerInterface> implements InvocationHandler{ List<ListenerInterface> listeners = new LinkedList<ListenerInterface>(); ListenerInterface proxy; @SuppressWarnings("unchecked") public ListenerList(Class<ListenerInterface> listenerInterfaceClass){; proxy = (ListenerInterface) Proxy.newProxyInstance(ListenerList.class.getClassLoader(), new Class<?>[]{listenerInterfaceClass}, this); } /** * convenience constructor shorthand. * * @param clazz - the class of the listener interface */ public static <T> ListenerList<T> create(Class<T> clazz){ return new ListenerList<T>(clazz); } public void add(ListenerInterface listener){ listeners.add(listener); } @Override public Object invoke(Object proxy, Method method, Object[] args)throws Throwable { for (ListenerInterface i: listeners) method.invoke(i, args); return null; } public ListenerInterface all(){ return proxy; }
The ListenerList<T> is a generic utility class, so it saves you code lines. It can be used everywhere the <T> is used: the syntax isn’t any more verbose compared to a plain usage of a <T> class directly. Plus it is null-safe and allows one-to-many relations.
Well, it’s all nice and so but has drawbacks.
Why do you assume that all listeners are interested in all events? In most cases they are not.
Also, event producer knows of the existence of listeners. So, your design is not following “Law of Demeter for Concerns” principle.
Publish–subscribe pattern would be a better choice.
The listeners would only implement event handlers for what they actually want to handle. This pattern does not prevent you from grouping listeners into logical groups – like ICarFactoryListeners, IIndustryFacilityListeners, IWorkSafetyListeners etc.
And yes, event producer must know of existence of listeners so he could fire the events the listeners are suppose to handle.
You know, it’s like the Button class is implemented in every component-based UI frameworks – every button knows there are clients and so every button is generating ButtonClicked() event.
That’s the problem of Telepathy Free World – if you don’t tell, you don’t expect it to be known. If you don’t inform your listener, the listener is not aware event has happened.
“Law of Demeter” does not forbid coupling – it promotes loose coupling. And that’s what producer/listener pattern is all about.