Table of Contents
The Observer Design Pattern intends to:
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. [c2.com]
In a typical implementation, the target of observation implements a Subject interface, and the observers implement an Observer interface:
Example 2.1. subject/observer API
interface Subject { void addObserver(Observer); void removeObserver(Observer); void notifyObservers(); } interface Observer { void notify(Subject subject); }
Observer instances register themselves with Subject instances using the Subject.addObserver() method, and Subject instances notify Observer instances of state changes using the Observer.notify() method.
Using object-oriented programming to implement this pattern removes any dependency of the subject implementation on the many possible observer implementations, however we're still left with dependencies on our subject/observer API in addition to logic that must be repeated over and over.
One possible OO solution utilizes inheritance. The class we want to be observable extends a Subject implementation and inherits its functionality:
Example 2.2. OO implementation that uses inheritance
class MyClassImpl implements MyClass extends SubjectImpl { void observedMethod() { // concrete logic notifyObservers(); } }
You can find an example parent class implementation in java.util.Observable. This problems with this approach are:
We can use delegation in place of inheritance and regain our ability to extend another class, however we're left with a lot of repeated coding, our subject/observer API must change to accomodate, and we're still left with mixed logic and a dependency on the subject/observer API:
Example 2.3. OO implementation that uses delegation
class MyClassImpl implements MyClass, Subject { Subject subject = ...; void observedMethod() { // concrete logic notifyObservers(); } void notifyObservers() { subject.notifyObservers(this) } void addObserver(Observer o) { subject.addObserver(o) } void removeObserver(Observer o) { subject.removeObserver(o) } }
We can combine the previous two approaches and utilize the Proxy Design Pattern to alleviate all of the issues with the exception of the repeated coding. We create a new class MyClassSubjectProxy that abstracts away the aforementioned dependencies:
Example 2.4. OO implementation that uses the proxy design pattern
class MyClassSubjectProxy implements MyClass extends SubjectImpl { MyClass myClass; MyClassSubjectProxy(MyClass myClass) { ... } void observedMethod() { myClass.observedMethod(); notifyObservers(); } }
This approach results in a great deal of repeated coding. We must implement a new proxy class per target class, per orthogonal concern. We must also explicitly hook the subject proxy when we create instances of MyClass that we want to be observable.