![]()
java.sun.com Solaris Developer Connection
|
![]() |
|
|
|
Technical Articles Index
|
|
|
|
Keeping Objects In Sync
Jim Coker, MageLang Institute
|
|
One of the nice things about programming in Java is that the language
and its class library are designed to provide practical solutions to real
object-oriented programming problems. In the object programming
community, many of these solutions have been well documented as design
patterns; each pattern is a generic solution to a common problem. One
such pattern is known as the Observer pattern, which is a solution to
the updating problem that arises when some objects have a dependency
relationship with others. Java provides a ready-made implementation for this
pattern through the Observable class and the Observer interface.
However, one occasionally runs into design issues that require careful thinking in order to create a solution appropriate to the task at hand. This article discusses the use of the Observable class, and the Observer interface, and shows how to solve some potential problems along the way. Introducing Observer Patterns A common scenario for using the Observer pattern involves a subject that has multiple views. Each view object needs to be updated whenever the subject changes. One example is a drawing program, where the subject is the internal representation of the drawing and views are different windows opened on the drawing. Any time you make a change in the drawing, each of the windows needs to be updated. The Observer pattern provides a way for each of the views to be notified whenever the subject has changed, without requiring that the subject know anything about the views, other than that they are observers. If the subject has to know more about the views, the program can quickly become difficult to manage--the code in the subject that handles updates becomes dependent on each of the views it must support. The Observer pattern solves this update problem as follows: The Subject (the object being observed) maintains a list of its observers. Whenever the subject makes a change to itself, it notifies all observers that a change has been made. It might also provide some generic change information with that notification. Each view gets the same notification. Usually this notification takes the form of a method call on the observer, with update information stored as a parameter to that call. The only thing that the subject knows about an observer is that it understands that method call. An Observer Pattern Implementation in Java The Java utilities package provides a ready-made implementation of the Observer pattern with the Observable class, which implements the updating behavior of the Subject and the Observer interface, which contains the update method to be called by the Observable, and can be easily implemented by any candidate observer objects. Here are the interfaces for Observable and Observer, with their methods grouped according to their function:
A Simplest Possible Example
Here is a very small example showing how the Observer pattern can
be implemented using Observable and Observer.
First create a Subject class with a simple data value that inherits from Observable.
Next a View class is created that accepts notifications
whenever its Subject is updated. For this example, the View
class just prints out a message.
Finally, you need to create a Subject and View, and link them together. This main method can be inserted as a method in the Subject class in order to run it as a standalone application.
A More Realistic Example The above example shows an Observer pattern implementation using Observable and Observer that is small enough to get an idea of how the pattern works, but is too small for one to run into any real-world problems, or get a good understanding of how powerful the Observer pattern is. The next example shows an implementation that requires some problem solving to reach a solution. It involves a counter with two views, a textual view, and a scrollbar view. The counter is a GUI widget comprising a label, a button, and an integer value. Whenever the button is pushed, the integer value is incremented and the label is updated. Both views will also be updated. Here is the finished applet:
Here is an early design of the Counter, one that does not expect to be viewed by other objects:
A reasonable question to ask at this point is: "Why not make the Counter object a non-GUI object (so it can inherit from Observable), and have the label and pushbutton part of the original Counter as one of the views?" Well, even though this Counter object could easily be reconfigured in that way, there are many cases where inheritance cannot be shifted around. One likely case is that the subject is already provided and can be subclassed, but cannot have its inheritance modified. Or the subject may be part of a complex data structure (such as an abstract syntax tree) where, as with AWT components, inheritance is used to define a hierarchy for organizing objects. This example shows a way to use the Observable object without having to inherit from it by delegating the behavior that Counter needs to a contained Observable object. Access to the Observable object is provided through an accessor method so that other objects can add observers. Here are the code additions to the Counter class:
However, in the process of implementing the delegation approach, another problem pops up. Two important methods of Observable are protected: setChanged and clearChanged; the intention being that only the observed subject should have direct control over this flag. But with the delegation approach shown above, the Counter will not be able to access the changed flags. To solve this problem, a new subclass of Observable is created to open up its interface and make those two methods visible publicly.
The New JDK 1.1 Event Model By the way, the observer/observable change notification structure is important for reasons beyond the scope of this article. The new event model for the upcoming JDK 1.1 is based on the same relationship between objects that change state and objects that are notified about state change. This new event model is referred to as the delegation-based event model (or delegation event model, for short).
The delegation model relies on event In the new AWT event model, event sources are like the observable objects presented here; listener objects play a role similar to observer objects. For more information on the new AWT delegation-based event model, read the section titled "Java AWT: Delegation Event Model" in " JDK1.1 AWT Enhancements." Back to the Regularly Scheduled Solution Here is the code for a textual view of the counter. It implements the Observable interface by providing the necessary update method. For this example, the second argument to the update method is expected to be the counter itself. Whenever update is called, the value of the counter is retrieved and the text field updated. It is up to the programmer to determine how the second argument is used to pass change information, but it should be the same for all views, and clearly documented in all views as well as the subject.
Now to wrap things up with a complete listing of the Counter class, as well as an Applet class, to demonstrate how to link the observers to the counter they observe. There is a link at the end of the article to the complete source file. Note also how the Counter method for event handling notifies the Counter's observers as soon as its value is changed.
Looking to Scalability in the Real World Using the Observer pattern helps to keep subjects in touch with their viewers, but what happens when a subject becomes complex, and the views have very different interests with regard to the subject? Consider an airline ticket reservation system, where the subject is a ticket object. One of many views might be a billing view transmitted to a credit card company, and another might be a seating view used to determine the all-important window or aisle allocation. If there is a change in seating, the ticket object changes, but the billing view does not need to be updated. This is an example of how the use of the Observer pattern can become complicated by real-world issues. The trick is to find solutions that retain the character of the original solution, and not to introduce more problems than they solve. One approach to the airline ticket problem is the use of aspects, as described in Design Patterns, by Gamma, Helm, Johnson, and Vlissides. Whenever an observer registers with a subject, it is done with respect to an aspect of that subject. Then, whenever the subject determines that an aspect of itself has changed, it notifies only those observers that are interested in that change. This makes coding the update methods easier, as they each have fewer changes to deal with, and makes the program more efficient, as only necessary update methods are called. For the ticket scenario above, the two aspects needed could be billing and seating. The addObserver method would take this into account:
Implementing this code requires some significant extensions to the Observable class, so you might like to complete that exercise in the privacy of your own home! References Arnold, Ken, and James Gosling. The Java Programming Language, Addison-Wesley, 1996. Gamma, Erich, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns, Addison-Wesley, 1994.
|
|
James Coker, MageLang Institute
|