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.
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
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
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
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
AsynchronousSubject implementation: executes each Observer
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
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.
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
that provides a parameterless
convenience method. The Observers are required to subclass the type Observer<NullEventData>.
In this case the
method will always receive the
Singleton instance of
to avoid the
potential risk of
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
are highly (really!) encouraged to be an
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