Sun Java Solaris Communities My SDN Account Join SDN
 
Article

Keeping Objects In Sync

 
 

Articles Index


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:


public class java.util.Observable
{
    // Constructors
    public Observable();    

    // here are methods for maintaining a
    // list of observers
    public void addObserver(Observer  o);
    public int countObservers();
    public void deleteObserver(Observer  o);
    public void deleteObservers();  

    // methods for maintaining a changed flag
    protected void clearChanged();
    protected void setChanged(); 
    public boolean hasChanged();    

    // methods for notifying observers of a change
    public void notifyObservers();  
    public void notifyObservers(Object  arg);
}


public  interface  java.util.Observer
{
   /** this method will be called by an observable 
       class whenever it wants to notify this observer 
       of a change.  The second argument can be used 
       to pass information about the change to the 
       observer */
    public abstract void update(Observable  o, Object  arg);
}


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.


public class Subject extends Observable {
  private String value;

  public void setValue(String s) {
    value = s;
    setChanged();   // set changed flag
    notifyObservers(value);  // do notification 
  }

}

When the Subject is changed through the setValue method two calls need to be made to ensure notification of all observers, one to set the changed flag, the other to notify observers. If the changed flag is not set, the notifyObservers call will do nothing. When notifyObservers is called, all observer objects will have their update method called. The Subject has no detailed information on the observers at all; it only has to make these two calls when it changes.

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.



class View implements Observer {
  public void update(Observable  o, Object str) {
    System.out.println("update: " + str.toString());
  }

}


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.


  public static void main(String[] args) {

    Subject s = new Subject();
    View v = new View();
    
    s.addObserver(v);
     // calling setValue on the subject
     // will trigger an update call to the view
    s.setValue("test value");   
  }

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 an early design of the Counter, one that does not expect to be viewed by other objects:


class Counter extends Panel {
  Label countLabel;
  Button incButton;
  private int _count = 0;

  /** create a new counter with a Label
      and an increment Button */
  public Counter() {
    setLayout(new BorderLayout());
    add("Center",countLabel = new Label("Count: 0"));
    add("South",incButton = new Button("Increment"));
  }

  /** return counter value */
  public int value() {
    return _count;
  }

  /** handle button push */
  public boolean action(Event evt, Object arg) {
     ... 
  }
}

To make the Counter observable, this example has two observers, a textual view, and a scroll view. The obvious way to achieve that behavior is to have Counter inherit from Observable. However, the counter shown already inherits from the AWT class Panel, which investigation reveals that it must do. Otherwise, due to type restrictions, it would not be able to properly fit into a component hierarchy. Defining the Counter so that it satisfies a component interface is not an option, as AWT does not define an interface (only classes) for GUI components, and Java does not allow an interface to extend a class. So a way must be found to add Observable behavior to the counter without using inheritance.

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:


class Counter extends Panel {
  ...
  Observable _observable;   // new data member

  public Counter() {
    ...
  // initialize observable in constructor
    _observable = new MyObservable();   
  }

  // new method to get access to observable
  public Observable observable() {
    return _observable;
  }

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.


class MyObservable extends Observable {
  
  public void clearChanged() {
    super.clearChanged();
  }

  public void setChanged() {
    super.setChanged();
  }
}

Now the Counter class can contain an Observable object (an instance of MyObservable) and access these methods. The change flag provided by the Observable object is still protected from users other than the Counter, as the Counter's observable method returns an object of class Observable, in which those methods are protected.

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 source objects and event listener objects. Objects that want to be informed about state changes in AWT components register themselves with event source objects by supplying an event handler or listener object to the source. This registration occurs by calling a listener registration method in the source with the listener object supplied as an argument. The source maintains a collection of all registered listener objects. When a state change occurs in the source, each listener object is notified by the source object calling a method with a predetermined name and signature in the listener object.

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.


class TextView extends TextField implements 
                                 Observer {
  
  public TextView(Counter c) {
    super(10);
    setEditable(false);
    setText(String.valueOf(c.value()));
  }

  /** update method called by observed Counter, 
      the second argument is the Counter object */
  public void update(Observable o, Object counter) {
   setText(String.valueOf(((Counter)counter).value()));
  }

}

The ScrollView object is very similar to the TextView, it just replaces the TextField object with a Scrollbar object.


class ScrollView extends Scrollbar implements 
                                   Observer {

  /** create a horizontal scrollbar 
      with a range from 0 to 10 */
  public ScrollView(Counter c) {
    super(Scrollbar.HORIZONTAL,0,1,0,10);
    setValue(c.value());
  }

  /** update method called by observed Counter, 
      the second argument is the Counter object */
  public void update(Observable o, Object counter) {
    setValue(((Counter)counter).value());
  }

}

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.


/** This is a counter class that delegates 
    observable behavior to a contained MyObservable 
    object.  A method is provided to access this 
    object. The counter object has a private integer
    variable to hold the counter value, a label
    to display it, and a button to increment the value.  
    Note that since this Counter is a GUI widget, 
    it inherits its behavior from the AWT class Panel.
    Since you also want it to be observable, that 
    behavior must be provided through delegation. */
class Counter extends Panel {
  Label countLabel;
  Button incButton;
  private int _count = 0;
  MyObservable _observable;

  public Counter() {
    setLayout(new BorderLayout());
    add("Center",countLabel = new Label("Count: 0");
    add("South",incButton = new Button("Increment"));
    _observable = new MyObservable();
  }

  public int value() {
    return _count;
  }

  /** make observable object accessible */
  public Observable observable() {
    return _observable;
  }

  /** handle clicks on the button, 
      notify observers of change */
  public boolean action(Event evt, Object arg) {
    if(evt.target == incButton) {
      _count++;
      countLabel.setText(String.valueOf(_count));
      _observable.setChanged();
      _observable.notifyObservers(this);
      return true;
    }
    return false;
  }
  
}


/** A simple applet to show the Counter and its two
  Observers, a TextView and a ScrollView */
public class ObserverTest extends Applet {
  Counter c;
  TextView tv;
  ScrollView sv;

  public ObserverTest() {
    setLayout(new BorderLayout());
    add("North",c = new Counter());
    add("Center",tv = new TextView(c));
    add("South",sv = new ScrollView(c));
	/* link the TextView observer to the 
           Counter observable */
    c.observable().addObserver(tv);
	// do same for the ScrollView object
    c.observable().addObserver(sv);
  }

}

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:


//declaration of addObserver and a couple of aspects
public synchronized void addObserver(
                             Observer o, int aspect);

public final static int BILLING = 1;
public final static int SEATING = 2;

// an example call to the new addObserver method
aTicket.addObserver(
              aBillingView,AirlineTicket.BILLING);
              

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.

Source Code

Oracle is reviewing the Sun product roadmap and will provide guidance to customers in accordance with Oracle's standard product communication policies. Any resulting features and timing of release of such features as determined by Oracle's review of roadmaps, are at the sole discretion of Oracle. All product roadmap information, whether communicated by Sun Microsystems or by Oracle, does not represent a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. It is intended for information purposes only, and may not be incorporated into any contract.