26.10. Observer/Publish-Subscribe/Delegation Event Model (GoF)
Another requirement for the iteration is adding the ability for a GUI window to refresh its display of the sale total when the total changes (see Figure 26.21). The idea is to solve the problem for this one case, and then in later iterations, extend the solution to refreshing the GUI display for other changing data as well.
Figure 26.21. Updating the interface when the sale total changes.
Name: | Observer (Publish-Subscribe) |
Problem: | Different kinds of subscriber objects are interested in the state changes or events of a publisher object, and want to react in their own unique way when the publisher generates an event. Moreover, the publisher wants to maintain low coupling to the subscribers. What to do? |
Solution: (advice) | Define a "subscriber" or "listener" interface. Subscribers implement this interface. The publisher can dynamically register subscribers who are interested in an event and notify them when an event occurs. |
Figure 26.22. The Observer pattern.
[View full size image]
- An interface is defined; in this case, PropertyListener with the operation onPropertyEvent .
- Define the window to implement the interface.
- SaleFrame1 will implement the method onPropertyEvent .
- When the SaleFrame1 window is initialized, pass it the Sale instance from which it is displaying the total.
- The SaleFrame1 window registers or subscribes to the Sale instance for notification of "property events," via the addPropertyListener message. That is, when a property (such as total) changes, the window wants to be notified.
- Note that the Sale does not know about SaleFrame1 objects; rather, it only knows about objects that implement the PropertyListener interface. This lowers the coupling of the Sale to the windowthe coupling is only to an interface, not to a GUI class.
- The Sale instance is thus a publisher of "property events." When the total changes, it iterates across all subscribing PropertyListeners , notifying each.
Figure 26.23. The observer
SaleFrame1 subscribes to the publisher Sale .Figure 26.24. The Sale publishes a property event to all its subscribers.
Note the approach to handing polymorphic messages in an interaction diagram, in Figure 26.24. The onPropertyEvent message is polymorphic; the specific cases of polymorphic implementation will be shown in other diagrams, as in Figure 26.25.
Figure 26.25. The subscriber SaleFrame1 receives notification of a published event.
[View full size image]
Why Is It Called Observer, Publish-Subscribe, or Delegation Event Model?
Originally, this idiom was called publish-subscribe, and it is still widely known by that name. One object "publishes events," such as the Sale publishing the "property event" when the total changes. No object may be interested in this event, in which case, the Sale has no registered subscribers. But objects that are interested, "subscribe" or register to interest in an event by asking the publishing to notify them. This was done with the Sale.addPropertyListener message. When the event happens, the registered subscribers are notified by a message.It has been called Observer because the listener or subscriber is observing the event; that term was popularized in Smalltalk in the early 1980s.It has also been called the Delegation Event Model (in Java) because the publisher delegates handling of events to "listeners" (subscribers; see Figure 26.26).
Figure 26.26. Who is the observer, listener, subscriber, and publisher?
[View full size image]
Observer Is Not Only for Connecting UIs and Model Objects
The previous example illustrated connecting a non-UI object to a UI object with Observer. However, other uses are common.The most prevalent use of this pattern is for GUI widget event handling, in both Java technologies (AWT and Swing) and in Microsoft's .NET. Each widget is a publisher of GUI-related events, and other objects can subscribe to interest in these. For example, a Swing JButton publishes an "action event" when it is pressed. Another object will register with the button so that when it is pressed, the object is sent a message and can take some action.As another example, Figure 26.27 illustrates an AlarmClock, which is a publisher of alarm events and various subscribers. This example is illustrative in that it emphasizes that many classes can implement the AlarmListener interface, many objects can simultaneously be registered listeners, and all can react to the "alarm event" in their own unique way.
Figure 26.27. Observer applied to alarm events, with different subscribers.
[View full size image]
One Publisher Can Have Many Subscribers for an Event
As suggested in Figure 26.27, one publisher instance could have from zero to many registered subscribers. For example, one instance of an AlarmClock could have three registered AlarmWindows , four Beepers , and one ReliabilityWatchDog . When an alarm event happens, all eight of these AlarmListeners are notified via an onAlarmEvent .
Implementation
Events
In both the Java and C# .NET implementations of Observer, an "event" is communicated via a regular message, such as onPropertyEvent . Moreover, in both cases, the event is more formally defined as a class, and filled with appropriate event data. The event is then passed as a parameter in the event message.For example:
class PropertyEvent extends Event
{
private Object sourceOfEvent;
private String propertyName;
private Object oldValue;
private Object newValue;
//...
}
//...
class Sale
{
private void publishPropertyEvent(
String name, Object old, Object new )
{
PropertyEvent evt =
new PropertyEvent( this, "sale.total", old, new);
for each AlarmListener al in alarmListeners
al.onPropertyEvent( evt );
}
//...
}
Java
When the JDK 1.0 was released in January 1996, it contained a weak publish-subscribe implementation based on a class and interface called Observable and Observer , respectively. This was essentially copied without improvement from an early 1980s approach to publish-subscribe implemented in Smalltalk.Therefore, in late 1996, as part of the JDK 1.1 effort, the Observable-Observer design was effectively replaced by the more robust Java Delegation Event Model (DEM) version of publish-subscribe, although the original design was kept for backward-compatibility (but in general to be avoided).The designs that have been described in this chapter are consistent with the DEM, but slightly simplified to emphasize the core ideas.
Summary
Observer provides a way to loosely couple objects in terms of communication. Publishers know about subscribers only through an interface, and subscribers can register (or de-register) dynamically with the publisher.Related Patterns Observer is based on Polymorphism, and provides Protected Variations in terms of protecting the publisher from knowing the specific class of object, and number of objects, that it communicates with when the publisher generates an event.