10

I have used some kind of "listeners" where I have an interface implemented by classes that need to be notified of some event (e.g.: CurrencyListener, with a method currencyUpdated(Currency currency)) Then, the object that needs to send a notification, has a list of listeners (List<CurrencyListener>) and just iterates over this list invoking the currencyUpdated(Currency currency) method.

The structure of this listener, is very similar to the Observer pattern, there is no Observer and Observable class, just the listener interface (CurrencyListener).

What are the advantages/disadvantages of using one approach over the other?

0

3 Answers 3

9

This is the Observer pattern - it's the exact same thing.

I have used some kind of "listeners" where I have an interface implemented by classes that need to be notified of some event (e.g.: CurrencyListener, with a method currencyUpdated(Currency currency))

In the Observer pattern, you have an abstraction (an interface or a base class) Observer which defines the things the observer can observe (or listen for). Your CurrencyListener is the Observer.

The other part of the pattern is the Observable, which is the object that sends notifications, which may or may not be itself an abstraction, or part of some hierarchy. I.e., the pattern does not require the ConcreteObservable subclases to be present. In the Go4 Patterns book, one of the roles for a ConcreteObservable is described as

"stores state of interest to ConcreteObserver objects."

If a ConcreteObserver can has no need for any special state, or can work with just the interface provided by the Observable base class, then there's no need for a ConcreteObservable.

Then, the object that needs to send a notification, has a list of listeners (List) and just iterates over this list invoking the currencyUpdated(Currency currency) method

The Observer provides methods for the Observables/Listeners to register with it, and it internally maintains a collection of these listeners. That's exactly what your object does.

Again, it's just the classic Observer pattern. The Observers and the Observables are independent of each other because they both depend on the abstraction provided by the Observer interface, and because they rely on the client code that uses them to hook up the Observers/Listeners with the Observable.

P.S. There's a number of variations on the pattern, and a number of features in different languages that are just the Observer pattern in disguise. For example, in C#, events are clearly a variation of the pattern. Another example of a variation would be what's usually called a Messenger (or an Event Aggregator, as Martin Fowler calls it), an can be found in some libraries and frameworks - it's a class that acts as as an intermediary between the observers and the observables. The structure is slightly different, but it is based around the Observer pattern; in fact, the Go4 book describes something similar near the end of the Implementation section in the description of the pattern (they call it ChangeManager).

2
  • Listener looks very close to an observer and has the same intent. But its design doesn't offer an abstract coupling between subject and observer. And this is an expected benefit according to GoF. So listener may almost be an observer. But it's not exactly the same. Recognizing the difference doesn't make listeners bad. Perhaps they are well adapted to this specific need. But not recognizing the difference might result in missed improvement opportunities.
    – Christophe
    Sep 21, 2018 at 6:07
  • @Christophe: I absolutely agree that understanding the impact of these differences and any variations is important, I think we just disagree about how flexible a realization of the pattern is allowed to be, and still be called the same pattern - I go into some more detail in the comment section of your own answer. Sep 21, 2018 at 16:55
4

In short

Your listeners are very close to be observers. However they seem to be tightly coupled to a very specific observed subject (e.g. Currency). In consequence, listening to other type of subjects would require additional listener interfaces and additional implementation of similar dispatching methods.

The original observer pattern, on contrary, ensures reusability and extendability by providing an abstract coupling between the observed subject (hereafter called observable) and the observers. This allows each side of the pattern to be specialized further independently.

In conclusion, your listeners are not quite observers.


In long, with plenty of details

Your design

I understand the following design:

  • Your concrete listeners all implement the CurrencyListener interface with a specific method CurrencyListener.currencyUpdated(Currency currency).
  • You have other listener interfaces for other kind of updates with other implementations.
  • The notification sender holds a list of CurrencyListener,
  • The notification sender iterates over this list and fires their currencyUpdated() methods.

Open issues:

  • You don't tell how you add/remove listeners to the notification sender. It's not a major question here, but the observer pattern could inspire you about this point.
  • You don't tell if the state of the notification sender is used by the listener (typical for an observer) or if currency is the object that the listener listens to (typical variant of the observer pattern, used when the same observer has to look at multiple observables). Hereafter, I will assume the latter.

Mapping your design with the observer pattern

Comparing your design with the Observer design pattern suggests that:

  • The notification sender is an Observable (unless Currency is)
  • The list of of listeners is a collection of Observer
  • The CurrencyListener is an Observer interface
  • The CurrencyUpdated() method is an update() method
  • The concrete listeners are concrete Observer

What's the difference ?

  • Your notification sender and your Currency are not an abstract interface but is a concrete class. They do not either seem to be a general class that offers an interface that would be abstract enough. This means that your observers all depend on that concrete class (strong coupling instead of abstract coupling).
  • The Observer pattern has a registration mechanism to add/remove observers to an observable. (But maybe you have too but didn't tell?)
  • Your CurrencyListener imposes the concrete type of object that the update function will receive. And this type seems to be very specific and closely linked to the notification sender. So implementing listeners for a new kind of observables requires each time a new interface.
  • On the contrary, the Observer.update() doesn't need to know the concrete Observable. This allows to decouple the implementations. The concrete Observer knows the concrete Observable and can get its state. A known variant is Observer.update(Observable o). So observers remain decoupled from a (too) concrete implementation of observable.

Conclusions:

Your listeners are fine. If you would have only one kind of observable subjects, they could even simplify the design.

But refactoring them using the observer pattern may help to decouple the parts, which could facilitate reuse and improve flexibility.

9
  • "Your CurrencyListener fixes the concrete type of object that the update function will receive." This isn't necessarily a problem if the data that is passed is general enough to be mostly relevant for every possible observer - like some contextual data for the event. Now, the diagram you linked to doesn't show this, but in the original description of the pattern there is a direct reference from the ConcreteObserver to the ConcreteSubject (this one is closer to the original). --- continues... Sep 20, 2018 at 23:39
  • ...continued --- The idea was to support layering; both ConcreteObservable and ConcreteSubject would be in the same lower level layer and so one can reference can the other (if it needs its state), but at the upper layer, client code would operate exclusively through the abstractions. Another thing this design supports is frameworks where the Subject and the Observer abstractions play the role of extension points. But note that, depending on what the code does, and what state it needs, ConcreteSubject is really optional. Sep 20, 2018 at 23:39
  • GoF clearly states that the benefits of the pattern is "an abstract coupling between Subject and Observer" (page 296). This is missing in the listeners and this is my main point in my answer. This also shows that for GoF the abstract vs. Concrete Subject/Observer is fully part of the design and not an accidental/optional construct.
    – Christophe
    Sep 21, 2018 at 5:47
  • 1
    @Christophe You seem to be hung up on class vs interface. The public members of a class form an Interface, and that can be just as loosely coupled as the members of an interface
    – Caleth
    Sep 21, 2018 at 11:45
  • 1
    @Christophe: I understand your point, but what I'm trying to say is that if the ConcreteObserver has no reference to (or doesn't mention the type of) the abstract Subject, then there's no extra coupling. I also understand your point about the Currency parameter, and if Currency is the Subject, then you are correct. But, in general, if it's a type that describes further the event, in the way where it makes sense to consider it part of the abstract Observer/Listener interface (because they are so closely related), then the level of coupling is practically the same. Sep 21, 2018 at 16:42
0

They are the same thing, the observer pattern is a pattern, listener is the implementation based on that pattern.

The other answers just pointed out the way you name your methods doesn't make your observer system reusable. So just try to use "update" instead of "updateCurrency" for naming the method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.