A consistent listener implementation
rnewman at mozilla.com
Mon May 5 12:03:58 PDT 2014
>> I attempted to design a generic ListenerManager that could be used to manage listeners
>> for any interface. There are definitely a few drawbacks (which I listed in the paste bin) but
>> I would just like to open up the discussion for creating something like this. Feedback please!
> I think your implementation is fine -- I've written essentially the same myself, in fact -- but I also think you've exposed the fact that expressing this thought in Java ends up looking like a bad expression of the thought. Fennec, Android Background Services, and even Android itself, have all elected to make interfaces one-shot deals. In the end, the abstraction does not win enough to make it worthwhile.
Further to this (I also didn't want this to get zero responses, but I've been mulling, and you beat me to it!):
"Listener" is kind of a bad name for the thing under consideration. Those are typically really *delegates*: an extension point, an individual thing that provides functionality according to a specified interface.
That doesn't really generically generalize to having multiple instances, at least not without getting into absurdly powerful Common Lisp-style generic functions and method combinations.
Consider: if one of these calls returns a result, which listener's result do you take? Can the same listener register with multiple 'talkers'? Pretty soon things get "Java silly": ReturnValueSummingListenerManagerFactory.
So I don't think the delegate pattern itself is something that needs to be fixed -- it's just being abused to achieve something that's better suited to a different pattern.
Instead, the leap is usually to an uncoupled system with either compile-time-checked or ad hoc string messages.
Senders broadcast messages along some bus, explicit (named channels) or implied (sendMessageToJava).
Listeners get handed messages asynchronously, based on some topic. If they wish they can take some action based on the contents, including sending their own messages. Listener registration is typically only loosely coupled with the sender lifecycle, and they typically don't return a value (because that's too much coupling).
We have this already, of course, because of our integration with Gecko, but we don't really use it for this.
In my experience, large compartmentalized systems (and the alternative to a large compartmentalized system is a horror show!) tend to grow some kind of internal messaging bus, for several reasons: lifecycle difficulty issues; interface coupling; difficulties in distributing an application across multiple processes or machines; and exactly this multiple-listener problem.
Eventually two or three components all want to know when a log file rotates, or a call starts, or an order is finished, and then you get an Enterprise Service Bus. In Android's case, you get LocalBroadcast and broadcast intents.
Which brings me to my conclusion: if we're reaching Peak Listener -- that is, if we're starting to roll up ad hoc notification systems, considering abusing sendMessageToJava, or discussing consistent listener implementations on the mailing list -- then perhaps it's time to start thinking about consistent use of messaging for inter-component communication.
More information about the mobile-firefox-dev