GoF Observer Pattern

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Componentized Observer Pattern

PerfectJPattern's componentized version of the Observer Pattern provides the following advantages:

  • Offers both Push and Pull models but I find the Push to be better for the following reasons. In the Push model Observers subscribe to a single event type therefore require one single type of event data (if any). The Push model is more general than the Pull model and completely decouples the Observers from the Subject. The Push model ensures that Observers receive exactly the information they require, this solution offers the following advantages:
    • Observers are not coupled to an external e.g. state-querying interface. This additional coupling exists in the Pull model shown in the GoF example where Observers needs to know where to look for when an event occurs e.g. the Subject.
    • Observers do not need to work hard to figure out what information changed.
    • Self-consistent state is guaranteed at all times. Since Observers receive a snapshot of the exact state at the moment of the notification, there is no risk that the Observers may get a wrong view of the state.
  • Provides a type-safe solution: Subject and Observer use genericity to identify what type of data is associated with an event e.g. IEventData subtype. Type-safety is enforced because Subject instances will only subscribe/attach Observers that comply with the correct event data type whereas e.g. Sun JDK's Observer implementation relies on an Object type-unsafe parameter.
  • Provides protection against transgressional Observers: Subject implementation protects itself and its clients from defective Observers that throw unchecked exceptions. Observers that during the handling of the update(...) method throw an unchecked exception are automatically detached. Not doing so would put Subject instances at risk of the unexpected behavior coming from faulty Observers.

UML Class Design



UML Sequence Diagram



Example

Extensions

  • ObserverProxy implementation: allows plain POJO types and free method signatures (currently only one parameter) to be targeted as result of a notification. This extension removes the otherwise needed dependency from PerfectJPattern allowing to directly target existing legacy code that does not subtype IObserver.
  • AsynchronousSubject implementation: executes each Observer update(...) method in a separate thread so that Subject's client is minimally blocked waiting for all Observers to be notified and handle their update(...) method, specially when there is a large number of Observer subscribed. This facility is also recommended because would protect Subject instances and its client from a possible greedy Observer e.g. an Observer whose update(...) implementation goes to an infinite loop. It is highly encouraged to use an Immutable Object type as event data.
  • Additional extensions are possible thanks to the pluggability of the componentized patterns e.g. Composite Subject or Synchronized Observers using the Composite and SynchronizedProxy componentized patterns.
    • Synchronized Observer: Observer with extended protection from concurrent access e.g. one Observer instance attaches/subscribes to multiple Subject instances that send notifyObservers(...) messages from separate threads; proxying the Observer instance with a SynchronizedProxy avoids race condition in the Observer update(...) method.
    • Composite Subject implementation: Offers Observer instances the convenience of not having to know and subscribe directly to many different Subject instances but to the single Composite one, in effect they achieve a multiple subscription transparently.

Notes

While the preferred model in PerfectJPattern's Observer is the Push model, it is absolutely not compulsory to associate data with an event. PerfectJPattern includes an implementation ParameterlessSubject that provides a parameterless notifyObservers() convenience method. The Observers are required to subclass the type Observer<NullEventData>. In this case the update(NullEventData anEventData) method will always receive the Singleton instance of NullEventData see NullObject Pattern to avoid the potential risk of NullPointerException if the Observer implementation tries reading the event data parameter.

Observes are also assumed not to need to know which Subject published an event. When it is a must to provide a different behavior per different Subject then it is recommended creating a different Observer type that will handle such event differently; this approach will consequence in a more reusable and maintainable design anyway.

Event data types e.g. subtype of IEventData are highly (really!) encouraged to be an Immutable Object type. Making event data Immutable offers the extra advantage not only of a self-consistent state notifying Observers but also no risk of race-hazards when Subject notifications are published from separate threads.