Main Page

Previous Next

Observable and Observer Objects

The class Observable provides you with an interesting mechanism for communicating a change in one class object to a number of other class objects. One use for this mechanism is in GUI programming where you often have one object representing all the data for the application – a text document, for instance, or a geometric model of a physical object– and several other objects that represent views of the data that are displayed in separate windows, where each shows a different representation or perhaps a subset of the data. This is referred to as the document/view architecture for an application, or sometimes the model/view architecture. This is a contraction of something referred to as the model/view/controller architecture and we will come back to this when we discuss creating Graphical User Interfaces (GUI). The document/view terminology is applied to any collection of application data – geometry, bitmaps, or whatever. It isn't restricted to what is normally understood by the term 'document'.

Click To expand

When the Document object changes, all the views need to be notified that a change has occurred, since they may well need to update what they display. The document is observable and all the views are observers. This is exactly what the Observable class is designed to achieve, when used in combination with an interface, Observer. A document can be considered to be an Observable object, and a view can be thought of as an Observer object. This enables the view to respond to changes in the document.

The document/view architecture portrays a many-to-many relationship. A document may have many observers, and a view may observe many documents.

Defining Classes of Observable Objects

You use the Observable class in the definition of a class of objects that may be observed. You simply derive the class for objects to be monitored, Document say, from the class Observable.

Any class that may need to be notified when a Document object has been changed must implement the interface Observer. This doesn't in itself cause the Observer objects to be notified when a change in an observed object occurs; it just establishes the potential for this to happen. You need to do something else to link the observers to the observable, which we'll come to in a moment.

The definition of the class for observed objects could be of the form:

public class Document extends Observable {
  // Details of the class definitions
}

The class Document here will inherit methods from the class Observable that operate the communications to the Observer objects.

A class for observers could be defined as:

public class View implements Observer {
  // Method for the interface
  public void update(Observable theObservableObject, Object arg) {
    // This method is called when the observed object changes
  }

  // Rest of the class definition...
}

To implement the Observer interface we need to define just one method, update(). This method is called automatically when an associated Observable object changes. The first argument that is passed to the update() method is a reference to the Observable object that changed and caused the method to be called. This enables the View object to access public methods in the associated Observable object, which would be used to access the data to be displayed, for example. The second argument passed to update() is used to convey additional information to the Observer object.

Observable Class Methods

The Observable class maintains an internal record of all the Observer objects related to the object to be observed. Your class, derived from Observable, will inherit the data members that deal with this. Your class of observable objects will also inherit nine methods from the class Observable. These are the following:

Method

Description

addObserver(Observer o)

Adds the object passed as an argument to the internal record of observers. Only Observer objects in the internal record will be notified when a change in the Observable object occurs.

deleteObserver (Observer o)

Deletes the object passed as an argument from the internal record of observers.

deleteObservers()

Deletes all observers from the internal record of observers.

notifyObservers (Object arg)

Calls the update() method for all of the Observer objects in the internal record if the current object has been set as changed. The current object is set as changed by calling the setChanged() method below. The current object and the argument passed to the notifyObservers() method will be passed to the update() method for each Observer object.

notifyObservers()

Calling this method is equivalent to the previous method with a null argument. (See setChanged() method below.)

countObservers()

The count of the number of Observer objects for the current object is returned as type int.

setChanged()

Sets the current object as changed. You must call this method before calling the notifyObservers() method. Note that this method is protected.

hasChanged()

Returns true if the object has been set as 'changed', and false otherwise.

clearChanged()

Resets the changed status of the current object to unchanged. Note that this method is also protected.

It's fairly easy to see how these methods are used to manage the relationship between an Observable object and its associated observers. To connect an observer to an Observable object, the Observer object must be registered with the Observable object by calling its addObserver() method. Once this is done the Observer will be notified automatically when changes to the Observable object occur. An observable object is responsible for adding Observer objects to its internal record through the addObserver() method. In practice, the Observer objects are typically created as objects that are dependent on the Observable object and then they are added to the record, so there's an implied ownership relationship.

This makes sense if you think about how the mechanism is often used in an application using the document/view architecture. A document has permanence since it represents the data for an application. A view is a transient presentation of some or all of the data in the document, so a Document object should naturally create and own its View objects. A view will be responsible for managing the interface to the application's user, but the update of the underlying data in the Document object would be carried out by methods in the Document object, which would then notify other View objects that a change has occurred.

Of course, you're in no way limited to using the Observable class and the Observer interface in the way in which we've described here. You can use them in any context where you want changes that occur in one class object to be communicated to others. We can exercise the process in a silly example.

Try It Out – Observing the Observable

We'll first define a class for an object that can exhibit change:

import java.util.Observable;

public class JekyllAndHyde extends Observable {
  String name = "Dr. Jekyll";

  public void drinkPotion() {
    name = "Mr.Hyde";
    setChanged();
    notifyObservers();
  }

  public String getName() {
    return name;
  }
}

Now we can define the class of person who's looking out for this kind of thing:

import java.util.Observer;
import java.util.Observable;

public class Person implements Observer {
  String name;              // Person's identity
  String says;              // What they say when startled

  // Constructor
  public Person(String name, String says) {
    this.name = name;
    this.says = says;
  }

  // Called when observing an object that changes
  public void update(Observable thing, Object o) {
    System.out.println("It's " + ((JekyllAndHyde)thing).getName() +
                       "\n" + name + ": " + says);
  }
}

We can gather a bunch of observers to watch Dr. Jekyll with the following class:

// Try out observers
import java.util.Observer;

public class Horrific {
  public static void main(String[] args) {
    JekyllAndHyde man = new JekyllAndHyde();  // Create Dr. Jekyll
    
    Observer[] crowd = 
    {
      new Person("Officer","What's all this then?"),
      new Person("Eileen Backwards", "Oh, no, it's horrible – those teeth!"),
      new Person("Phil McCavity", "I'm your local dentist – here's my card."),
      new Person("Slim Sagebrush", "What in tarnation's goin' on here?"),
      new Person("Freaky Weirdo", "Real cool, man. Where can I get that stuff?")
    };

    // Add the observers
    for(int i = 0; i < crowd.length; i++)
      man.addObserver(crowd[i]);

    man.drinkPotion();                 // Dr. Jekyll drinks up
  }
}

If you compile and run this, you should get the output:

It's Mr.Hyde
Freaky Weirdo: Real cool, man. Where can I get that stuff?
It's Mr.Hyde
Slim Sagebrush: What in tarnation's goin' on here?
It's Mr.Hyde
Phil McCavity: I'm your local dentist – here's my card.
It's Mr.Hyde
Eileen Backwards: Oh, no, it's horrible – those teeth!
It's Mr.Hyde
Officer: What's all this then?

How It Works

JekyllAndHyde is a very simple class with just two methods. The drinkPotion() method encourages Dr. Jekyll to do his stuff, and the getName() method enables anyone who is interested to find out who he is. The class extends the Observable class so we can add observers for an object of this class.

The revamped Person class implements the Observer interface, so an object of this class can observe an Observable object. When notified of a change in the object being observed, the update() method will be called. Here, it just outputs who the person is, and what they say.

In the Horrific class, after defining Dr. Jekyll in the variable man, we create an array, crowd, of type Observer to hold the observers – which are of type Person, of course. We can use an array of type Observer because the class Person implements the Observer interface. We pass two arguments to the Person class constructor: a name, and a string that is what the person will say when they see a change in Dr. Jekyll. We add each of the observers for the object man in the for loop.

Calling the drinkPotion() method for the object man results in the internal name being changed, the setChanged() method being called for the man object, and the notifyObservers() method that is inherited from the Observable class being called. This causes the update() method for each of the registered observers to be called, which generates the output. If you comment out the setChanged() call in the drinkPotion() method, and compile and run the program again, you'll get no output. Unless setChanged() is called, the observers aren't notified.

Now let's move on to look at the java.util.Random class.

Previous Next
JavaScript Editor Java Tutorials Free JavaScript Editor