1

I'm reading about design patterns from both "Head First Design Patterns" and the original "Elements of reusable software etc" and I'm finding some confusion right at the start, in the Observer pattern. For reference, the Wikipedia article uses the same diagram.

enter image description here

Both books depict a diagram with two abstract classes/interfaces, Subject and Observer, distinct from their implementation, ConcreteSubject and ConcreteObserver. This is justified with keeping the two components loosely coupled. Specifically, "Head First Design Patterns" says:

Changes to either the subject or an observer will not affect the other. Because the two are loosely coupled, we are free to make changes to either, as long as the objects still meet their obligations to implement the Subject or Observer interfaces.

I can't quite figure an example where the Subject would need to be abstract, but I'm sure there are some applications out there. My real issue is in the concrete implementations: there is a directed association from the ConcreteObserver to the ConcreteSubject. It's explained that this is due to the update() function (and the Observer interface) not knowing about the Subject, so the ConcreteObserver has to interact with the ConcreteSubject to inquire about the nature of the change.

Doesn't this directed association negate the Subject abstraction? If ConcreteSubject changes ConcreteObserver must follow, contrary to the quoted paragraph. What use is there in keeping Observer and Subject loosely coupled if ConcreteObserver has to depend on ConcreteSubject?

8
  • link to the articles?
    – Ewan
    Commented May 29 at 6:29
  • @Ewan They are books, not articles, so I cannot link the exact explanation. I'm sure there are free PDF versions but it's pirated content, I don't think I can link it here.
    – Maldus
    Commented May 29 at 6:45
  • You can add links to e-store selling books to illustrate the point. Then at least owners of books would have the reference. Or just add academia style citations.
    – Basilevs
    Commented May 29 at 6:52
  • Just because the diagram has tight coupling between the ConcreteSubject and ConcreteObserver doesn't mean that this is the only way to implement the pattern. Keep in mind that the GoF patterns book where this pattern was originally popularised, was written 30 years ago and is mostly about illustrating concepts rather examples of 'best practice'. In reality most Observer/Observable implementations don't look anything like the diagrams from GoF design patterns book. If you're using this book as a guide to writing code then you're taking away completely the wrong message from it. Commented May 29 at 7:19
  • 1
    I think you are just interpreting the arrow incorrectly. It doesn't mean a direct type reference, just that the subject calls/messages the observers (via their interface/abstract)
    – Ewan
    Commented May 29 at 9:24

4 Answers 4

1

Lets use the wikipedia diagram.

enter image description here

Here the diagram clearly shows observers calling the getState method on the concrete Subject1 class. A method which is not on the subject base class/interface, so it must be tightly coupled to the concrete subject.

However, You can see the flaw in this implementation, you would expect an observer to be able to observe more than one type of thing!

If we look at the example implementations in the same article, none of them implement this getState method (I don't count javascript). Instead they use a shared class for passing information in the update call.

This is still coupling, but looser than a direct reference. At the end of the day you need to be able to pass the subject's state to the observers, and that state can be more complex than a primitive type

By passing a Payload class in the update method you can pass some generic information about the subjects state, such as a string message, new and old value etc

In this case the abstract or interface Subjectdoes allow you to create concrete Observers and Subjects where the only coupling is via the Payload class. ie I can make a concrete observer which can subscribe to any concrete subject, only referencing the interfaces/abstract types for each, plus the payload class

Indeed, even in the original diagram, if i choose not to make the getState call in the observer. I can subscribe to any subject, I just don't receive any state information.

The Payload object can be made into a generic type, or you can use down casting in an observer to get around the loose coupling if you really wanted to pass extra information.

For example

https://learn.microsoft.com/en-us/dotnet/api/system.iobservable-1?view=net-8.0

or

https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/how-to-implement-property-change-notification?view=netframeworkdesktop-4.8

2
  • > you would expect an observer to be able to observe more than one type of thing! And how would you do that? Are you implying that the abstract Subject should also include the parts of the state that are relevant for the Observers? I understand that the Observer must be coupled with the Subject to work. What I don't get is why there is an abstract Subject if the abstraction must eventually be circumvented - thus becoming useless.
    – Maldus
    Commented May 29 at 9:58
  • let me add a bit more
    – Ewan
    Commented May 29 at 10:19
0

Subjects and Observers don't exist by themselves

The abstract Subject interface doesn't exist to decouple ConcreteObserver from ConcreteSubject, but to decouple the source of notification triggers away from Subjects.

Technically, notification triggers are not part of the pattern, but obviously they need to exist somewhere in the application otherwise there'll be no notifications. The pattern also doesn't describe how registrations happen either, the book just assumes you've got that covered.

The fact the book doesn't decouple ConcreteObserver from ConcreteSubject doesn't mean that you're not allowed to change it up in your own implementation - by all means, Programmers thinking for themselves and adapting a solution to their own needs is generally expected. In a similar vein, there would be nothing wrong with sending the Subject's current state in the notification instead of having the Observer query the Subject. I would argue that these are small implementation details however; changing these doesn't fundamentally stop it being the Observer pattern.

Observer Example - Desktop GUIs

Consider a typical example in a GoF-era desktop GUI application. These apps usually contain a Windowing message/event system - for example:

  • An event loop (usually the application's main thread) consuming input events from the operating system (e.g. keyboard/mouse input)
  • A collection of UI control instances (Concrete Subjects) able to handle input events.
  • An event dispatcher which is able to resolve each input event to a specific UI Control and trigger the notification.

In this scenario, the event system only really cares about some abstract notify() interface and a way of identifying the target control. It doesn't care whether the actual concrete type of a target happens to be a Button, ListBox, TextBox, etc. so long as they are all able to handle the same event.

"Patterns are not building blocks"(*1)

(*1) If you haven't seen this excellent answer on the topic of Design Patterns, then it's worth reading: Choosing the right Design Pattern

Perhaps the most important lesson is not to worry about the minutiae of any particular implementation, but the general principle of decoupling the source of an event away from application behaviours. It's something you'll find in most libraries/frameworks handling events and push notifications.

If you're comfortable with this, then any approach you use to make it happen are entirely up to you, and any variations are fine too. There are near-limitless different ways to implement some form of Observer pattern, many of which are arguably better than the GoF example; especially when considering features available in different programming languages and frameworks.

1
  • @Downvoter - please let me know what you think could be improved with this answer; any feedback helps improve the site for everybody! Commented May 31 at 21:26
-2

Things that interact must be coupled in some way. Much of software engineering is about how to keep the coupling to the minimally necessary one-way coupling.

Implementing state change propagation in the naive way almost inevitably results in unnecessary two-way coupling. The Observer design pattern is really just the least complicated way of not doing that.

1
  • 1
    Wouldn't collapsing Subject and ConcreteSubject in a single (concrete) object achieve the same coupling with one less interface? Even in a situation where I need to pass an abstract Subject the observer has to know about the concrete implementation to do any meaningful work - unless it only needs the notification with no other information, but that's quite a niche use case IMO.
    – Maldus
    Commented May 29 at 7:32
-2

The abstract observer and subject allow to specify the general interaction principle (contract) that all subjects and observers must implement, i.e. registration and notification, while leaving details of what an observer can do with a subject that notified it to their concrete implementations.

In their original book, the GoF did not distinguish explicitly interfaces in this pattern, but used abstract classes instead (at the time of publishing that book, Java was not born yet). In this model the abstract class had the advantage to also allow to implement some repetitive registering mechanisms, that in the Head First book and other variants need to be implemented for every implementation of the interface.

The navigable association back from concrete observer to concrete subject allows the observer to know more about the subject.

There are variants of this pattern where the back navigable association is defined in the abstract class (or in the interface, via a parameter of the update call). But these variants impose that the concrete observers can only use the general subject interface unless they do some nasty risky downcasting.

Let's take an example of observer in the MVC architecture. The abstract observer/subject contract specify the general principle that a view (observer) can be notified and observe changes in the model (e.g. Person, Cars or other business objects). But in reality, not every view would be interested in all the model elements (e.g. some views display person information, others display car informations). So most of the time the view needs to know more about the subject that it observes to do useful things (e.g person view needs to know that a person subject has a first and a last name, car view needs to know which car characteristics could be displayed). This is why the back link is generally defined only in the concret observer.

You could as well think that all these abstractions are unnecessary overhead and go directly at the concrete level, implementing classes that have implicitly the interface. This would probably work but creates a strong coupling between concrete subject and observer, thus reducing reuse possibilities, and making exteremely difficult to have an observer that is able to register to several subjects (practical example: a reusable MVC controller elemnt that just observes model objects to see if they changed to activate/deactivate a save menu - not possible without the abstraction layer)

2
  • 2
    My confusion isn't about the dependency between observer and subject, it's in the double interface. What is the point for an abstract Subject if every ConcreteObserver must know the ConcreteSubject? No element cares about Subject in this diagram.
    – Maldus
    Commented May 29 at 7:46
  • @Maldus ok, edited
    – Christophe
    Commented May 29 at 11:47

Your Answer

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

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