49

I've noticed that my dependency injected, observer-pattern-heavy code (using Guava's EventBus) is often significantly more difficult to debug than code I've written in the past without these features. Particularly when trying to determine when and why observer code is being called.

Martin Oderski and friends wrote a lengthy paper with an especially alluring title, "Deprecating the Observer Pattern" and I have not yet made the time to read it.

I'd like to know what is so wrong with the observer pattern and so much better about the (proposed or other) alternatives to lead such bright people to write this paper.

As a start, I did find one (entertaining) critique of the paper here.

Jeff Axelrod
  • 27,676
  • 31
  • 147
  • 246

3 Answers3

38

Quoting directly from the paper:

To illustrate the precise problems of the observer pattern, we start with a simple and ubiquitous example: mouse dragging. The following example traces the movements of the mouse during a drag operation in a Path object and displays it on the screen. To keep things simple, we use Scala closures as observers.

var path: Path = null
val moveObserver = { (event: MouseEvent) =>
   path.lineTo(event.position)
   draw(path)
}
control.addMouseDownObserver { event =>
   path = new Path(event.position)
   control.addMouseMoveObserver(moveObserver)
}
control.addMouseUpObserver { event =>
   control.removeMouseMoveObserver(moveObserver)
   path.close()
   draw(path)
}

The above example, and as we will argue the observer pattern as defined in [25] in general, violates an impressive line-up of important software engineering principles:

Side-effects Observers promote side-effects. Since observers are stateless, we often need several of them to simulate a state machine as in the drag example. We have to save the state where it is accessible to all involved observers such as in the variable path above.

Encapsulation As the state variable path escapes the scope of the observers, the observer pattern breaks encapsulation.

Composability Multiple observers form a loose collection of objects that deal with a single concern (or multiple, see next point). Since multiple observers are installed at different points at different times, we can’t, for instance, easily dispose them altogether.

Separation of concerns The above observers not only trace the mouse path but also call a drawing command, or more generally, include two different concerns in the same code location. It is often preferable to separate the concerns of constructing the path and displaying it, e.g., as in the model-view-controller (MVC) [30] pattern.

Scalablity We could achieve a separation of concerns in our example by creating a class for paths that itself publishes events when the path changes. Unfortunately, there is no guarantee for data consistency in the observer pattern. Let us suppose we would create another event publishing object that depends on changes in our original path, e.g., a rectangle that represents the bounds of our path. Also consider an observer listening to changes in both the path and its bounds in order to draw a framed path. This observer would manually need to determine whether the bounds are already updated and, if not, defer the drawing operation. Otherwise the user could observe a frame on the screen that has the wrong size (a glitch).

Uniformity Different methods to install different observers decrease code uniformity.

Abstraction There is a low level of abstraction in the example. It relies on a heavyweight interface of a control class that provides more than just specific methods to install mouse event observers. Therefore, we cannot abstract over the precise event sources. For instance, we could let the user abort a drag operation by hitting the escape key or use a different pointer device such as a touch screen or graphics tablet.

Resource management An observer’s life-time needs to be managed by clients. Because of performance reasons, we want to observe mouse move events only during a drag operation. Therefore, we need to explicitly install and uninstall the mouse move observer and we need to remember the point of installation (control above).

Semantic distance Ultimately, the example is hard to understand because the control flow is inverted which results in too much boilerplate code that increases the semantic distance between the programmers intention and the actual code.

[25] E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design patterns: elements of reusable object-oriented software. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, 1995. ISBN 0-201-63361-2.

Adrian Ratnapala
  • 5,485
  • 2
  • 29
  • 39
Jeff Axelrod
  • 27,676
  • 31
  • 147
  • 246
  • 2
    I wonder if the weaknesses listed here are inherent to the observer pattern. I've just read through a description of the MobX library, which I believe uses the observer pattern, but that seems to address a good number of the issues described above. For example, it seems that MobX does include a guarantee of the consistency and "freshness" of values, and the order in which they update. See [here](https://hackernoon.com/the-fundamental-principles-behind-mobx-7a725f71f3e8). Curious what others think... – fraxture May 30 '18 at 13:46
  • 4
    I won't comment on all of them, but many can be alleviated by using the correct pattern, i.e. Change Manager for the Observer in the case above where you are defining a path. Change Manager is a Mediator. So I think your example is a bit of a straw man. – Robinson Sep 07 '18 at 10:15
  • The link in the answer is not accessible anymore... but the one in the question is (and supposedly they should be the same) – Jefferson Quesado Sep 09 '19 at 17:12
  • 1
    Has anyone an example how this case is better using the now recommended `PropertyChangeEvent` – Benjamin Peter Apr 06 '20 at 09:08
  • @jeff thank you. what pattern do you prefer to observer? Delegation through closure? – ScottyBlades Sep 16 '20 at 06:24
21

I believe the Observer pattern has the standard drawbacks that come with decoupling things. The Subject gets decoupled from Observer but you cannot just look at its source code and find out who observes it. Hardcoded dependencies are usually easier to read and think about but they are harder to modify and reuse. It's a tradeoff.

As to the paper, it does not address the Observer pattern itself but a particular usage of it. In particular: multiple stateless Observer objects per single object being observed. This has the obvious drawback of the separate observers needing to synchronize with each other ("Since observers are stateless, we often need several of them to simulate a state machine as in the drag example. We have to save the state where it is accessible to all involved observers such as in the variable path above.")

The above drawback is specific to this kind of usage, not to the Observer pattern itself. You could as well create a single (stateful!) observer object that implements all the OnThis, OnThat,OnWhatever methods and get rid of the problem of simulating a state machine across many stateless objects.

Rafał Dowgird
  • 43,216
  • 11
  • 77
  • 90
  • But the observers in the example *do* clearly have side effects as mentioned in your quote. (See all the manipulations of the `path` def). – Jeff Axelrod Jul 24 '12 at 13:46
8

I will be brief because I'm new to the topic (and didn't read that specific article yet).

Observer Pattern is intuitively wrong: The Object to be observed knows who is observing (Subject<>--Observer). That is against real-life (in event-based scenarios). If I scream, I have no idea who is listening; if a lightening, hits the floor... lightning doesn't know that there is a floor till it hits!. Only Observers know what they can observe.

When this kind of of things happen then software use to be a mess -because constructed against our way of thinking-. It is as if and object knew what other objects can call his methods.

IMO a layer such as "Environment" is the one in charge of taking the events and notifying the affected. (OR mixes the event and the generator of that event)

Event-Source (Subject) generates events to the Environment. Environment delivers the event to the Observer. Observer could register to the kind of events that affects him or it is actually defined in the Environment. Both possibilities make sense (but I wanted to be brief).

In my understanding the Observer Pattern puts together environment & subject.

PS. hate to put in paragraphs abstract ideas! :P

  • 1
    Here http://www.marco.panizza.name/dispenseTM/slides/exerc/eventNotifier/eventNotifier.html I have found the diagram which basically says the same: -Publisher (Subject) -Subscriber (Object) -EventService (Environment) (1998) After all "common sense" leads to it and one way or another we implement these kind of middle-layer when programming. Problem comes when the Pattern motivates or misleads. – Luis Fernando Robledano Dec 13 '12 at 11:14
  • Subjects do not know their observers (perhaps only through base class). The Design Pattern book also mention a change manager. This is somewhat similar as your environment. Imho the Observer pattern is great for notification of (independent) GUI elements, but the pattern has some serious drawbacks as well: spurious notifications while subject or business layer is not in a consistent state; subject changes by a observer during a notification update. – gast128 Feb 20 '15 at 11:48
  • 1
    A few points here, the subject does not know any thing about the observers, we can achieve this by abstract interface. – Farruh Habibullaev Mar 04 '18 at 20:33