Chapter 23

Using Observers


CONTENTS


Introduction

Consider an applet-call it Frammitz 1.0-comprising a large data structure, a sophisticated user interface, and some complex control logic. How would you go about constructing such an applet? As a good object-oriented developer, you immediately say: "Aha! That data structure should go in its own class! While I'm at it, I'll put the logic in there too!" Uttering the battle cry of the followers of OO-"Encapsulate!"-you separate the data from the interface. You then reap the many expected benefits-increased abstraction and readability, and reduced complexity (which helps during maintenance, too). By partitioning the system into interface and behavior, you reduce the coupling between classes. You also pave the way for later reuse of either piece. By keeping the interface separate, you avoid the "event-spaghetti" code that plagues many object-oriented GUI environments. So, you have two classes: a UI in the form of an applet, and the data with its rules. Because the rules for handling the data are important, you make sure that the only way to access the data is through methods that enforce the rules. Otherwise, a malicious object could circumvent the model's logic. Now you have a full-fledged model and its user interface.

Consider the user interface a bit more closely. The user interface arbitrates between the user and the model. So, it makes sense to think of the user interface from two perspectives. First, what about the model needs to be presented? Second, what tasks will a user perform on that model? Thus, thinking in terms of a model, what does a UI really need to do? Well, it needs to present the model in a comprehensible way. Most of the time, it also needs to allow the user to interact with the model. An unspoken assumption is that the interface always accurately reflects the contents of the model. That is, the state of the interface should always be consistent with that of the model. All right then, we need two sets of methods in the user interface, a group of display methods to present the model, and a group of control methods, which can alter the model. (These usually end up being paint and action methods.) So, we code the paint and action methods to access Frammitz's associated data object. The paint methods read the model's state from one set of accessor methods. The action methods change the model's state through a different set of accessor and logic methods.

Flash forward a few weeks (or maybe days). For the most part, all went well on Frammitz version 1.0. You ran into a little sticky spot when implementing the control methods. You see, any time the model changed, you had to redisplay the new data. Because the control methods were changing the model, they had to call the display methods to update the display. So, it got a little messy-the control methods were calling the display methods all over the place-but it works. Version 1.0 is out the door (although that particular metaphor probably needs to be updated for the Webbed World).

Disaster! Frammitz 1.0 is a wild success. Your users love it, and your boss loves that your users love it. Why is that a disaster? Because your users love it so much that they are requesting all kinds of wild enhancements-things like totally new ways of looking at the model, or totally new ways of interacting with it, or new rules for the model itself. "While you're at it, how hard would it be to add multithreading?" Sound familiar? Odds are, you've been here at least once. Nothing fails like success, because now you must find a way to add new views and new rules. Suddenly, what seemed like "a little sticky spot" starts to look like exponentially proliferating methods, with all the maintenance and stability problems that that implies. Worse yet, because this applet is constantly being shipped all over the world in byte-codes, adding even a few kilobytes to your code size can turn the most loyal of users against you. Frammitz 2.0 starts to look unlikely.

Now, imagine that a simple design change in the beginning could make changing views simple. Or that it could make new interaction modes possible, without doubling the number of methods and interconnections in your class. Even better, imagine that your users could create their own views, without affecting your model! Now you're cooking with gas! Decoupling your classes is the secret. You can achieve complete decoupling in many cases by using a class and an interface defined in package java.util. It provides a class, Observable,and an interface, Observer, that can make it happen. Observables collaborate with Observers to automatically keep Observers up to date. The Observers can use these notifications to synchronize their state with that of the Observables, or to update their display, or to trigger some other action.

This chapter will present the parts of the Observer/Observable pair and describe their interaction with the aid of a simple example. By examining Observer/Observable as a design pattern, it will discuss the implications of using the Observer pattern. It will also explore an important object-oriented architecture enabled by Observer, which originated in Smalltalk-80, called Model-View-Controller or MVC. A more sophisticated example will illustrate the flexibility provided by using MVC. A final section will briefly discuss other applications of Observer, outside of the GUI realm. (Observer in boldface refers to the pattern name. Observer in monospaced type refers to the interface defined by java.util.Observer, and ConcreteObserver refers to any class that implements Observer. Don't worry, each of these is discussed in detail later on.)

Interface Observer

The Observer interface is shown in Listing 23.1. It is quite simple, consisting of only one method declaration-update. By defining update within a class, you make that class an Observer.


Listing 23.1. The definition of interface Observer.
package java.util;

public interface Observer {
    void update(Observable o, Object arg);
}

Note
By convention, an interface name usually ends in "-able," but not this time. Here, the class name ends in "-able," but the interface name does not. Admittedly, this is not terribly consistent, but imagine how confusing it would be if the "Observable" were actually the thing doing the observing, and the "Observer" were the thing being observed!

Observers subscribe to Observables. One Observer can subscribe to many Observers, but there is only one update() method, which all of the Observables will call. The Observer can use the first argument to update() to tell which Observable sent the notification. The second argument is available for whatever use the Observable supplies. Generally, arg will be one of two things:

Using arg for an application-specific protocol can optimize the updates greatly; however, it increases the coupling between the classes.

Remember that Java considers interfaces to be types. Suppose a class PassMe extends Applet and implements Observer. Then, an instance of PassMe can legitimately be passed to any of the following functions:

public void GiveMeAnApplet(Applet a);
public void GiveMeAnObserver(Observer o);
public void GiveMeAPassMe(PassMe p);

Moreover, GiveMeAnObserver can take an instance of any other class that implements Observer, whatever its class type or superclass may be. This first-class rank of interfaces is the key to Java's Observer mechanism. (For more details on interfaces and inheritance, see the sidebar "Interface vs. Implementation Inheritance" later in this chapter.)

Class Observable

The public and protected interface to class Observable is shown in Listing 23.2.


Listing 23.2. The definition of class Observable.
package java.util;

public class Observable {
    public synchronized void addObserver(Observer o);
    public synchronized void deleteObserver(Observer o);
    public synchronized int countObservers();
    public void notifyObservers();
    public synchronized void notifyObservers(Object arg);
    public synchronized void deleteObservers();
    protected synchronized void setChanged();
    protected synchronized void clearChanged();
    protected synchronized boolean hasChanged();
}

Remember that this is a class, so your data items and models will use Observable by inheriting from it. Although they are not declared as final, there is usually no reason to override the provided implementations for these methods. You can see that this class is well protected for use by multithreaded applications. Indeed, allowing changes from any thread to be immediately reflected in other threads is one of the best uses for the Observer/Observable pair.

The addObserver() and deleteObserver() methods allow an Observer to subscribe or unsubscribe to this Observable. Notice that they each take a parameter of type Observer. This implies that they can be passed instances of any class that implements Observer. Internally, Observable maintains a vector of all the Observers that have been added to it. When notifyObservers() is called, the update() method of each subscribed Observer is invoked with an optional argument to indicate what changed.

The setChanged(), clearChanged(), and hasChanged() methods allow a subclass of Observable to keep a "dirty flag" to indicate when the Observers need to be notified. Typically, an Observable will call setChanged() as soon as a state change occurs. It will not call clearChanged() until it notifies its Observers. Although these methods are protected, you may sometimes find it useful to define pass-through methods in your subclass. Allowing another object to set and clear this flag can sometimes speed up notifications.

Notice that there are two forms of notifyObservers(), one with an Object argument and one with no arguments. The simpler form of notifyObservers() just calls notifyObservers(null). (That is why the simpler form is not synchronized.) For simple data items, the first form is adequate. Sometimes it will be useful to provide the Observer with more information about what has changed. In a complex Observable, it may be costly to redisplay everything, so the Observer would like to optimize its redisplay. Often, this is impossible, because it can be difficult to deduce what aspect of an Observable has changed. Because you can pass any Object to notifyObservers(), your particular Observable can define a protocol for its Observers. For example, an Observable that maintained a vector of Points might pass the changed Point to its Observers. Be careful, however, that this protocol does not couple the Observer too tightly to the Observable. Your Observable should consider this argument a hint to the Observer, something which can help the Observer, but could safely be ignored.

Putting Them Together

Neither Observer nor Observable is useful by itself. Used together, Observer and Observable create a publish-subscribe protocol. The Observable publishes notifications, to which Observers subscribe. It sends notifications to any and all interested Observers, without knowing or caring what the concrete classes of the Observers are. Any number of Observers can subscribe to a single Observable. Also, each Observer can subscribe to more than one Observable. This creates a one-to-many dependency between objects so that when the Observable changes state, all of its dependents (Observers) are notified and can update themselves automatically.

You cannot use either the Observer interface or the Observable class as is. In order to make use of this publish-subscribe protocol, you must subclass Observable and implement Observer. In the rest of this chapter, Observer refers to the interface, whereas ConcreteObserver refers to a class that implements Observer. Likewise, Observable refers to the class Observable, whereas ConcreteObservable refers to a specific subclass of Observable. Figure 23.1 illustrates the relationships involved between Observer, its ConcreteObserver, the ConcreteObservable under scrutiny, and Observable.

Figure 23.1: Relationships between Observer, ConcreteObserver, Observable, and ConcreteObservable.

The Observable knows its Observers but does not know what they are. This important distinction arises because Observer is an interface. Each Observer can be from vastly different, unrelated classes. Because they implement the correct interface, Observable can send the notification without any regard for what each Observer does on receipt of the message.

In Figure 23.2, you can see the flow of events that occurs when an Observable's state is changed. The initial "set" message can come from an Observer or from some other controlling class. The accessor methods that cause state changes must set the "dirty" flag by calling setChanged(). At some point notifyObservers() gets called, often from within the accessor method itself. Sometimes, another object will call it. Both methods have advantages. (See the section "Observer Implementation Trade-Offs" later in this chapter.) In this case, the ConcreteObservable itself calls notifyObservers(). Within notifyObservers(), the ConcreteObservable calls the update() method on each ConcreteObserver in turn. The order in which the Observers are notified is undefined. (Even though you can look at the source code for Observable and see exactly what the order will be, that is an implementation detail, not part of Observable's contract! The abstraction of Observable does not define the notification order, so you should not rely on the implementation. After all, implementations change without warning.) After the Observers have been notified, the ConcreteObservable calls clearChanged(). It is very important that all of the crucial data items in a ConcreteObservable are private. If any were made public, other objects could modify the members without calling setChanged() or notifyObservers(), completely defeating the purpose of using Observers in the first place.

Figure 23.2: Object interaction diagram.

Note
Sometimes, it will be useful to add public methods to your Observable to pass through to setChanged() and clearChanged(). For example, if your controller is in the process of making several small changes to different parts of the Observable's state, you may want to defer the notifyObservers() call until all of the changes are done. Avoiding superfluous updates can boost performance significantly. By eliminating unnecessary updates, you also eliminate the annoying display flicker from the extra updates.
On the other hand, adding that sort of knowledge to your controller increases the coupling between the controller and the Observable.

A Simple Example: The SlideValue Applet

Let's use Observer and Observable to create a simple applet. This applet will consist of a scrollbar and a text field. We want the text field to always reflect the value of the scrollbar. For this example, the relationship will be one-way. That is, changes to the text field do not need to be reflected in the scrollbar. Adding the updates in the other direction is just an application of the same techniques used here. Figure 23.3 shows the SlideValue applet in action.

Figure 23.3: The SlideValue applet in action.

The most basic approach, and the one taken most often by new designers, would be to construct an applet class with a scrollbar, a text field, and a private integer. The class would have an event handler for the scrollbar changes, which would update the internal integer and call a method to put a string in the text field. Although this approach has the benefit of keeping everything in one class, there really is no way to separate the behavior from the interface. Any future changes in either presentation or behavior would require touching every part of the class that deals with presentation and behavior.

A more sophisticated approach separates the data, the view, and the controller. By using a separate data class, derived from Observable, we can implement whatever logic is necessary to maintain a consistent state. In addition, we can change that logic without having to make any corresponding changes in the user interface. Because the text field needs to update itself when the data value changes, we will derive a class from java.awt.TextField, which implements Observer.

The Observable

In this case, the ConcreteObservable is Value, shown in Listing 23.3. It mainly consists of the value itself, val, and two methods to set and return the value. Look carefully at the set() method. The extra code in this method sets the changed flag, notifies any interested Observers, and then clears the changed flag. (If the changed flag is not set, notifyObservers() will do nothing.) As an alternative to the boolean flag provided by Observable, your ConcreteObservable class can also override the hasChanged() method to perform more sophisticated detection.

When Value calls notifyObservers(), it uses the form without parameters. Because there is only one item of interest (one state variable) in this class, passing anything to the notification would be somewhat superfluous.


Listing 23.3. Value.java, the ConcreteObservable for the SlideValue example.
/*
** Value.java
**
** ConcreteObserver class for the SlideValue example
**
*/

import java.util.Observable;

class Value extends Observable {
    private float val;

    public void Value(float v) {
        val = v;
    }

    public void set(float v) {
        setChanged();
        val = v;
        notifyObservers();
        clearChanged();
    }

    public float get() {
        return val;
    }
}

The Observer

For this example, the only view into the data is a simple text field. For this reason, our ConcreteObserver will be derived from java.awt.TextField, which will implement Observer. Observer is an interface, so any class can implement it, whatever its heritage. Listing 23.4 shows View.java.


Listing 23.4. View.java, the ConcreteObserver for the SlideValue example.
/*
** View.java
**
** ConcreteObserver for the SlideValue example
**
*/

import java.awt.TextField;
import java.util.Observer;
import java.util.Observable;
import Value;

class View extends TextField implements Observer {
    public void update(Observable o, Object arg) {
        Value v = (Value)o;

        setText(Float.toString(v.get()));
    }
}

Because the update method is passed an Observable, View must cast the Observable to a Value. In this example, View will only be used to observe Value, so this cast is safe. In a production class, you would want to use the instanceof operator to ensure safe casting. In fact, if you have several ConcreteObservables you want to observe from a single ConcreteObserver, a good approach would be to define a protocol by which the ConcreteObserver can query the state of the Observables.

The Controller

The controller for SlideValue is embedded directly in the applet class. In general, the controller should have its own class also. In this case, however, we are primarily interested in the interaction between Value and View. Therefore, embedding the controller in the applet class helps clarify that interaction. (See Listing 23.5.)


Listing 23.5. SlideValue applet definition.
/*
** SlideValue.java
**
** Applet container for SlideValue example
**
*/

import java.applet.*;
import java.awt.*;
import Value;
import View;

public class SlideValue extends Applet {
    private Value     value;
    private View      view;
    private Scrollbar controller;

    public void init() {
        // Set up a layout manager
        setLayout(new BorderLayout());
        resize(200, 55);

        // Create the value, view, and controller.
        value = new Value();
        view = new View();
        controller = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 0, 100);

        // Add the view to the model's notification list.
        value.addObserver(view);

        // Put the view and the controller into the UI.
        add("North", controller);
        add("South", view);
    }

    public boolean handleEvent(Event e) {
        if (e.target instanceof Scrollbar) {
            value.set((float)controller.getValue());
        }
        return super.handleEvent(e);
    }
}

This simple applet really just creates the components and defines the relationships. It uses the Border Layout Manager described in Chapter 17, "Programming the User Interface," to place the View and ScrollBar objects. For this example, the ConcreteObserver and ConcreteObservable are both created by the applet itself, and the Observer is added to the notify list right here. In general, the applet itself does not need to create all of the relationships. You can even have the constructor for the Observer take an Observable as a parameter, although you will usually not want to introduce another class dependency. By constructing the relationship outside of the two classes being related, you reduce the coupling between those classes and allow them to be related in different ways as the occasions arise.

The HTML Container

The containing page for this applet is extremely basic. Listing 23.6 shows the HTML code. It simply gives the applet a region of the page and provides links to the Java code.


Listing 23.6. HTML container page for the SlideValue example.
<title>SlideValue</title>
<hr>
<applet code=SlideValue.class>
</applet>
<hr>
<a href="SlideValue.java">SlideValue.java</a><br>
<a href="View.java">View.java</a><br>
<a href="Value.java">Value.java</a><p>

SlideValue Interaction

Figure 23.4 shows the interaction diagram for the SlideValue applet. The chain of events begins when the user acts upon the scrollbar. This results in a call to SlideValue.handleEvent(), which sets the value of Value. At this point, the controller is finished. After set is called, SlideValue leaves the picture. None of the interaction from this point on involves the main applet. Instead, everything happens between the Value and its Observers (just the single TextField view this time around).

Figure 23.4: Event interaction diagram for SlideValue.

Value publishes its state, and View subscribes to it. By relating the classes this way, all changes to Value's state are automatically reflected in View. Additional views can be added easily. Further, the applet code does not need to know anything about the state of Value or Value's implementation. It does not need to know anything about how View creates its display or gets updates. By reducing the amount of knowledge one class needs about another, we have reduced the coupling. To illustrate, let's enhance SlideValue to include a second view, a gauge that shows the Value as a percentage. (See Figure 23.5.)

Figure 23.5: SlideValue in action, with a GaugeView added.

The new view will be a subclass of Panel, and, of course, it will implement Observer. Listing 23.7 shows the new class. Although GaugeView does maintain an integer, lastKnownPercentage, which is fetched from Value, it is not really copying the state of Value. It is processed after retrieving it from Value (a cast from float to int) and treated as read-only elsewhere. A member used this way is more like a cached value than a state variable. The cached value is guaranteed to be up-to-date, because any time the Value changes, GaugeView gets notified! An equally valid technique would be to store a reference to Value in GaugeView and get the value directly from Value whenever it is needed. In this case, the added overhead of the extra method calls far outweighs the added storage for the extra integer. For a more complex model, that might not be the case.


Listing 23.7. The new GaugeView class.
/*
** GaugeView.java
**
** A new ConcreteObserver for the SlideValue example
**
*/

import java.awt.Panel;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Color;
import java.util.Observer;
import java.util.Observable;
import Value;

class   GaugeView extends Panel implements Observer {
    private int lastKnownPercentage;

    public void update(Observable o, Object arg) {
        Graphics  g = getGraphics();
        Value     v = (Value)o;

        lastKnownPercentage = (int)v.get();

        repaint();
    }

    public void paint(Graphics g) {
        Dimension    d = size();
        FontMetrics  fm = g.getFontMetrics();
        int          textHeight = fm.getAscent() + fm.getDescent();
        StringBuffer s = new StringBuffer();

        s.append(lastKnownPercentage);
        s.append("%");

        g.setColor(getForeground());

        g.fillRect(0, 0,
            (int)(d.width * (lastKnownPercentage / 100.0)),
            d.height);

        g.setColor(Color.lightGray);
        g.drawString(s.toString(),
            (int)((d.width - fm.stringWidth(s.toString()))/2),
            (int)((d.height - textHeight)/2 + fm.getAscent()));
    }
}

Listing 23.8 shows the modifications to SlideValue in bold. Notice that the only changes required to add a new view were in the code that creates the view itself! The Value and the existing View did not change at all-not one line.


Listing 23.8. The modified SlideValue applet class with changes shown in bold.
/*
** SlideValue.java
**
** Applet container for SlideValue example
**
*/

import java.applet.*;
import java.awt.*;
import Value;
import View;
import GaugeView;

public class SlideValue extends Applet {
    private Value     value;
    private View      view;
    private GaugeView gauge;
 
   private Scrollbar controller;

    public void init() {
        // Set up a layout manager
        setLayout(new BorderLayout());
        resize(200, 55);

        // Create the value, views, and controller.
 
       value = new Value();
        view = new View();
        gauge = new GaugeView();
 
       controller = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 0, 100);

        // Add the views to the model's notification list.
 
       value.addObserver(view);
        value.addObserver(gauge);
        // Put the views and the controller into the UI.
 
       add("North", controller);
        add("South", view);
        add("Center", gauge);
 
   }

    public boolean handleEvent(Event e) {
        if (e.target instanceof Scrollbar) {
            value.set((float)controller.getValue());
        }
        return super.handleEvent(e);
    }
}

No way could you add a new feature to a monolithic applet that easily! Because we used Observers, the new view dropped right in without disturbing the existing classes.

Interface Versus Implementation Inheritance
Java supports several advanced object-oriented constructs, including interfaces. An interface is a collection of method signatures, without actual implementations. A class can declare that it implements one or more interfaces. Think of an interface as a contract between your class and other objects. By implementing interfaces, your class commits to providing methods with the same signatures as in the interface. Java treats interfaces as first class types; you can declare variables of interface types just as you would declare variables of class types. Although a class can only inherit from one base class, it can implement several interfaces. The main distinction is that interfaces do not override implementations. (In other words, you cannot call "super" methods in an interface method.)
Interfaces are useful when you need to require certain behavior from client objects, but do not necessarily want all of the client objects to share a common parent. For example, it is impractical to require that all Observers inherit from a common base class. That would preclude you from subclassing TextField, Panel, Thread, and so on as Observers.
There are two situations that become somewhat confusing. First, what happens when a class implements two interfaces with the same method? Because no implementation comes with either interface, there is no conflict. The class simply provides one actual method that can be called from either interface. Second, what happens when you subclass a class that implements an interface? The subclasses automatically implement the interface, through the parent class's method. In fact, you can override the parent class's method (even calling the "super" method). Your class will still legally implement the interface. (For an example of this usage, see the classes TwoDView and XYView in the AppletCAD example later in this chapter.)

Flexible Object-Oriented Design

Designing flexible, reusable object-oriented software is difficult in any language. Balancing the needs of the problem at hand with the desire for reuse is difficult, even for the most seasoned designer. In fact, most of the thought involved in creating any software goes toward solving problems during design. With a good design, the actual coding becomes largely mechanical. So, how can we create good designs, designs that can evolve with the requirements of a project?

Experienced object-oriented designers know better than to solve every problem from scratch. Rather, when faced with a problem, experienced designers will compare it to similar problems they have faced in the past and apply a solution that worked before. They tend to work with a group of tried-and-true designs, adapting them to the problem at hand. This is not the same as code reuse. Actual code reuse is rare and vastly overstated. Instead, they reuse designs by extracting recurring patterns of classes, relationships, and interaction. These design patterns solve specific problems that occur over and over again across a wide variety of domains. By recognizing and identifying a design pattern, you become able to reuse that design by applying it to new problems. Design patterns make it easier to take a successful architecture of classes, communicating objects, and relationships to a new problem.

In general, design patterns are not code. Instead, they collect a common interaction and a common set of objects at an abstract level. The actual coding depends on the particular problem. Design patterns are not a template mechanism, like the templates in C++. They are also not classes that you can directly apply. Moreover, design patterns are not language-specific. The pattern itself is completely abstract. It is the kernel of a solution, not the solution itself. In the case of Java, there are classes and interfaces that allow you to apply some patterns easily, but this is an instance of a pattern. This distinction-between the pattern and one implementation of the pattern-is the same as the distinction between a class and an instance of that class. One is abstract, describing how to construct something. The second is the product of that construction.

The designers of the Java classes collectively represent decades of experience with object- oriented designs. Not surprisingly, the built-in Java classes include instantiations of several design patterns. One particularly well-known design pattern is Observer. Also not surprisingly, the instantiation of the Observer pattern comprises the Observer interface and the Observable class. From here on, this chapter will refer to the aggregate of Observer, Observable, and their interaction as Observer. (Remember, Observer in boldface refers to the pattern name, while Observer in monospaced type refers to the interface defined by java.util.Observer. ConcreteObserver refers to any class that implements Observer.)

Observer as a Design Pattern

The need to partition an object-oriented system into a collection of interdependent classes leads to a common difficulty. Often, related classes must maintain a consistent state. A label must reflect the contents of a string, or a choice box must only display the choices allowed by a policy determined by another object. Tight coupling between classes is one (poor) way to achieve coherence. Many early object-oriented designs relied upon classes having such carnal knowledge of each other. This approach works. It is easy to design and code. On the other hand, the tight coupling severely limits-or eliminates-their reusability. Such systems do not grow or evolve well.

Many GUI toolkits solve this problem by separating the interface classes from the underlying model. As in the earlier example, classes defining the application data and the interface can each be reused or replaced independently. Multiple views of the application data can coexist. For all intents and purposes, the views will behave as if they have full knowledge of each other. Changes to one view are automatically reflected in the others. Figure 23.6 shows a conceptual example of this partitioning. In this example, three views into the application data need to be kept synchronized with the data itself. Such a system will behave as if the views are tightly coupled, when, in fact, quite the opposite is true.

Figure 23.6: Conceptual model of the model/interface partitioning and interaction.

This type of interaction is characteristic of the Observer design pattern. As described earlier, the key parts of Observer are the Observer and the Observable. The Observable may have any number of Observers, drawn from any number of potentially unrelated classes. Whenever the Observable's public state changes, all of its Observers are notified of the change. All Observers are notified. If an Observer initiated the state change, it will still receive the notification and should postpone its update until it receives the notification. When an Observer receives a notification, it queries the Observable in order to synchronize its state with that of the Observable.

This pattern is sometimes referred to as Publish-Subscribe or Dependents. Each of the three names reflects a different perspective on the interaction. (Chris Warth, the Java developer who implemented this pattern, evidently preferred Observer.) The Observable publishes notifications, to which the Observers subscribe. Or, looking at it another way, the Observers are dependent on the Observable.

Whatever name you prefer, there are several situations in which this pattern is applicable:

Some Implications of Using Observer

This pattern allows you to independently reuse or replace Observers and Observables. This tremendous level of flexibility does not come without a cost, however. The most obvious cost is complexity. By using Observer, you do add a certain degree of complexity to your system. There will be at least one additional class, if nothing else. For anything other than a small system, the added complexity is a small price to pay, both in terms of code size and development time. Furthermore, to anyone familiar with the Observer pattern, the intent of your code will be immediately obvious. (This is a bonus awarded for using a well-known pattern!)

Observer carries a more insidious cost you must be aware of. Because the notification protocol is very generic, an Observer often has no details on what part of the Observable's state changes, or who made the change. Without adding to the protocol, it can be difficult for an Observer to deduce the nature and origin of a change. Adding such a protocol can improve performance, sometimes dramatically, by eliminating unnecessary work in the Observers and by allowing Observers to ignore notifications in which they have no interest. At the same time, however, adding the extra protocol increases the coupling between the classes.

Another possible solution to this problem would be to allow an Observer to subscribe to specific parts of an Observable. The Java team evidently decided that the benefits of that approach were outweighed by the added conceptual and code complexity required. It would not be difficult, however, to create your own implementation of the Observer pattern that did allow partial subscriptions.

Because multiple Observers do not know about each other's existence, they may be ignorant of the ultimate cost of a simple state change. A small, relatively harmless-seeming change can trigger many updates, even a cascade of updates, because an Observer of one item may be an Observable to its own Observers. Observers do not know the implementation details of the Observable, so a particular Observer cannot predict which changes will trigger notifications.

The coupling between Observables and their Observers is minimal. The Observers do need a little more information about the Observable, but still relatively little. Because an Observable knows nothing about the concrete class of an Observer, Observers can be modified or added long after the initial design.

An added bonus that Java awards you for using Observer is that there is often no need to download some of the Observers. If your applet includes ten potential views, but the user only calls for two of them, there is no need to download the other eight classes. This kind of runtime customization can really improve the perceived performance of your applet.

Interactions

The publish-subscribe interaction is depicted in Figure 23.7. This basically follows the interaction you observed in the SlideValue example. Several questions arise about this diagram. First, how are these relationships created? Also, when should notifyObservers() be called? Observer does not specify the answers. Instead, when you implement the pattern, you decide what is the best option for this application. (This tailoring is a fundamental characteristic of a design pattern, and it is one of the things that distinguish a pattern from an actual design. A pattern describes the core of a solution to a problem-you can use it a million times over and never do it the same way twice.)

Figure 23.7: Basic Observer/Observable interaction during a notifyObservers.

Figure 23.8 depicts a cascaded notification, in which ConcreteObserver2 is also an Observable for ConcreteObserver3. You might use an arrangement like this when dealing with a layered system. Because the coupling between Observers and Observables is very loose, you can span architectural layers without violating the system layering. In Figure 23.8, for example, ConcreteObservable might be at the data access layer, ConcreteObserver2 at the logic layer, and ConcreteObserver3 at the interface layer.

Figure 23.8: Cascaded notification when an Observer is also Observable.

It is possible to have mutually dependent Observables that are both also Observers. These objects would interact exactly as in Figure 23.7. This time, however, the classes each need some knowledge about the other. They must be careful not to trigger a notification during their update methods, or a vicious cycle of notifications will ensue. (This can really be a problem in multithreaded applications, because one thread will go completely out to lunch and eat processor cycles, but everything else will seem to work fine. In fact, other Observers will often still behave correctly.) Such a storm of notifications can be exceedingly difficult to detect.

Observer Implementation Trade-Offs

This section discusses some of the decisions made by the Java team. It also explores some of the implementation choices you will need to make when using Observer.

Observables need to know about their Observers. Observables could maintain a simple array, an associative array (a hash table), a linked list, and so on. The Java team chose to use a subclass of Vector to maintain the Observers. Strictly speaking, this is part of the implementation details of Observable, and we should ignore such details. However, because we are inheriting from Observable, implementation details such as this have some direct implications. By using Vector to contain the Observers, we can have an arbitrary number of them, while still maintaining a good memory/speed balance. Thus, an Observer with no Observables incurs minimal overhead. Sequential and random access to the Observer list allows good performance when adding or deleting Observers. Therefore, we can create Observables with many Observers with confidence that performance will not degrade unduly.

In some implementations of Observer, Observers maintain references to their Observables. This leads to a number of additional synchronization problems, especially when deleting Observables. To avoid this problem, the Java implementation of Observer has no reference to the Observable. One consequence of this decision is that Observers cannot access their Observable's state outside of the update method. As in the GaugeView class in the SlideValue example, the Observer must cache any information it needs in methods like paint. An alternative approach would have update in your ConcreteObserver store the Observable reference passed to it, at which point we come full circle to the problem of dangling references.

Sometimes, it makes sense for a single Observer to depend on multiple Observables. For example, a complicated form may incorporate data from multiple data sources, or a three- dimensional rendering may include multiple models. For situations like this, the Observable reference passed to update can disambiguate what is being updated. If each Observable of interest is from a separate class, the instanceof keyword can provide enough information. If some of the Observables are from the same classes, your Observer may need to keep references to all of its Observables and compare the object references.

Does it seem as if many of these issues imply that Observers should keep references to their Observables? You may ask why the Java team did not incorporate these references into Observer. Well, first of all, Observer is an interface-it has no data members. More importantly, however, the Java implementation is the most basic and generic one. Nothing in the Java implementation precludes adding an Observable reference in your Observers. If the implementation already had such references, you would not be able to remove them from your Observers. In other words, you can embellish the existing classes at will, but it is impossible to delete items from parent classes. This philosophy is apparent in much of the Java class architecture. Wherever possible, the Java developers have provided enough of a framework to allow the embellishment, but not require it.

One of the questions you must answer when using Observers is "Who calls notifyObservers()?" There are basically two options:

If you decide to have client objects call notifyObservers(), your Observable will need pass-through methods for the setChanged() and clearChanged() methods. The notifyObservers() methods in Observable check hasChanged(). If hasChanged() returns false, notifyObservers() does nothing.

An issue of consistency relates to this question of responsibility. It is very important that Observables publish notifications only when their internal state is consistent. Otherwise, Observers may update their state from an inconsistent Observable. Intentional violations of this rule are not a big problem-they are easily caught-but inherited operations can lead to violations. For example, consider the following code fragment:

public class ParentClass extends Observable {

    public void setState(float value) {
        m_ParentState = value;
        setChanged();
        notifyObservers();
        clearChanged();
    }
}

public class ChildClass extends ParentClass {

    public void setState(float value) {
       super.setState(value);     // notifyObservers gets called.
       setChanged();
       m_ChildState += value;     // Too late!
       clearChanged();
    }
}

By the time m_ChildState is updated, the notifications have already been sent, while the state was inconsistent. Sending another notification will not help, because there is no telling what the Observer did with the bogus values in the meantime. If making all state-changing methods in your Observable final is an option, you should do so. If not, you can use another technique that is more or less bulletproof. The trick is to remove the notification code from the methods that get inherited. Or, stated another way, inherit only the state modification code. The following code fragment illustrates this technique:

public class ParentClass extends Observable {

    public final void setState(float value) {
        internalSetState(value);
        notifyObservers();
        clearChanged();
    }
    protected void internalSetState(float value) {
        m_ParentState = value;
        setChanged();
    }
}

public class ChildClass extends ParentClass {

    protected void internalSetState(float value) {
       super.internalSetState(value);
       m_ChildState += value;
    }
}

Multithreaded Observers
A multithreaded application is an ideal environment for using Observer. Often, controllers and views execute in separate threads. Using an Observable allows the controller object, in its thread, to make changes in a synchronized manner. At the same time, the Observers automatically get notified. Be careful, because update() gets called in the context of the controller that made the changes. If you intend to use Observers in a multithreaded application, all of your update() methods should be synchronized. In addition, if your Observer needs to do a lot of work in response to an update, it should do it in the context of the Observer's thread and not tie up the controller's thread. Take a look at the following code fragment for an idea of how this would work.
Public class MyObserver implements Observer {
   boolean bUpdateNeeded;

   public void update(Observable o, Object arg) {
      updateSync(o, arg);
   }

   private synchronized void updateSync(Observable o, Object arg) {
      wait();
      bUpdateNeeded = true;
      notify();
   }
   …
   public void RunsInThread() {
      /* Wait for notice that an update is needed. */
      while(bUpdateNeeded == false) {
         try {
            wait();
         } catch (InterruptedException e) {
         }
      }
      bUpdateNeeded = false;
      notify();
      /* Do the update */
   }
}

The Model-View-Controller Paradigm

Many GUI toolkits (aside from visual tools like Visual Basic or Powerbuilder) partition the classes into user interface and application data classes. (These are sometimes referred to as the presentation and logic layers.) Smalltalk-80 bestowed upon us the Model/View/Controller (MVC) architecture for presentation and logic layers. MVC refines the idea of presentation and logic layers by introducing another partition. An MVC triad consists of cooperating objects: the Model, the View, and the Controller. The Controller and View are typically user interface objects. The Controller allows a user to modify the Model. Once modified, the Model notifies the View that a change has occurred. The View queries the Model and displays the updated information. By separating the objects this way, the interactions are greatly simplified, the coupling is reduced, and the responsibilities of each object are very clear. The Model never has a user interface component. The View never changes the Model. The Controller never directly interacts with the View. Figure 23.9 shows the connections in an application with one Model, two Views, and two Controllers. You can see that the upper half of the figure looks just like the connections between an Observable and two Observers. Of course, that is hardly a coincidence. In many ways, you can consider MVC as one of the earliest occurrences of the Observer pattern.

Figure 23.9: An example of a Model-View-Controller architecture.

Although Java does not directly refer to MVC, the built-in Observer mechanism forms the foundation upon which you can build MVC applications. Java applications built with an MVC architecture evolve well over time. They can easily adopt new interfaces, new models, and new displays. Ideally, each component of MVC can be replaced or reused independently of the whole.

The Model (The Observable)

The first, and most pivotal, component of MVC is the Model-the aggregation of the application data and its internal rules. The model encapsulates all behavior that the data has on its own. It enforces consistency, handles persistence, and, most importantly, ensures that all accesses to its state variables go through accessor methods. The Model publishes notifications whenever its state changes. Many Views can subscribe to these notifications.

In Java, a Model inherits from Observable. It can contain other objects and implement whatever interfaces are helpful. The Model should not directly communicate with a View or a Controller. Likewise, the Model should not create network connections. (Odds are that the network connection would have been used for one of three purposes: to report the Model's status, to initiate some action on the remote side as a result of the Model's state, or to query an external system and change the Model's state accordingly. The first two should be done by Observers anyway, and the last one is a Controller's job.) The Model should always be able to stand alone, without the rest of the application around it. It should be completely independent. The Model always behaves in a reactive mode.

The View (The Observer)

The View is the visual display of the Model, or a relevant portion of the Model. A spreadsheet might have chart, graph, and tabular views into the underlying Model. The View does nothing more than display the Model; it never manipulates it. A View must always reflect the current state of its Model. Therefore, a View subscribes to its Model's notifications.

Of course, a Java View implements the Observer interface. It can be a subclassed Component, Container, or Panel. Although there can be Observers that are not Views (that is, have no user interface), all Views are Observers. Each View should be aware that there may be many other Views, and take care to be "friendly." Update is called synchronously. Therefore, if an update requires long calculations or extensive processing, it should spawn a new thread to do the work. That way, the Model can continue with its notifications.

The Controller

The Controller determines how the Model will react to the user interface. Most of the time, the Controller is the user interface. The Controller does not directly manipulate the View, but the View reflects its changes to the Model.

A Java controller can be any graphical component or group of components. Most of the time, the controller itself will be a Panel subclass, with a number of components within it.

The Advantages of MVC

The three advantages of using MVC are flexibility, flexibility, and flexibility. You can create new views for a model without modifying the model in the slightest. You can create new controllers without changing the model or the views. You can create and destroy any number of views at runtime. In short, MVC applications can grow and evolve, without turning into "code jungles."

The objects in an MVC application interact in a very well-defined way, which reduces bugs and enhances maintainability. The role of each object is clear. Because the number of interactions is kept to a minimum, the relationships are not fragile. Changes to one class frequently do not require changes to other classes. In an MVC application, the architecture is clear and obvious to even a casual observer.

An interesting point about MVC applications is that they are generally devoid of a mediator or controller object. One object does set up the relationships, and that object "owns" the others, in the sense that it is responsible for destroying them. But really, there is not a single overseer. This has good and bad consequences. On the plus side, by avoiding a single overseer, MVC objects interact quickly and simply. On the down side, it is sometimes unclear which object owns the MVC objects.

The AppletCAD Applet

Now, let's apply the full Observer pattern within the context of the Model/View/Controller architecture. This applet will use multiple views of a common data model, with a distinct controller. Figure 23.10 shows the applet in action. The main container uses a grid layout to hold four panels. Three of the panels are views that implement Observer. The fourth panel is the controller. Each view presents a two-dimensional projection of three-dimensional points, similar to the editing mode found in many CAD packages. The controller allows the user to add a new point at the specified coordinates.

Figure 23.10: The AppletCAD applet with several points added.

The Applet

Listing 23.9 shows the code for the applet itself. Event-handling code is conspicuous by its absence. The applet class constructs the model, views, and controller. It creates the relationships. Then, it gets out of the picture. After the MVC components are created, they handle all of the interaction. There is no further need for the applet to involve itself.


Listing 23.9. AppletCAD class.
/*
** AppletCAD.java
**
** Applet container for AppletCAD example
**
*/

import java.applet.*;
import java.awt.*;
import PointList;
import Point3D;

public class AppletCAD extends Applet {
    private XYView       xyView;
    private YZView       yzView;
    private XZView       xzView;
    private CoordControl coordControl;
    private PointList    pointList;

    public void init() {
        // Create the model
        pointList = new PointList();

        // Create the views
        xyView = new XYView();
        yzView = new YZView();
        xzView = new XZView();

        // Create the controller
        coordControl = new CoordControl(pointList);

        // Subscribe the views to the model.
        pointList.addObserver(xyView);
        pointList.addObserver(yzView);
        pointList.addObserver(xzView);

        // Add the views and controller,
        // using a layout manager
        setLayout(new GridLayout(2, 2));
        add(xyView);
        add(yzView);
        add(xzView);
        add(coordControl);
    }

    public Insets insets() {
        return new Insets(5, 5, 5, 5);
    }
}

The Observable

Point3D, shown in Listing 23.10, is used by the data model, the views, and the controller. It is simply a three-dimensional analogue to Point. It has public members x, y, and z, which are all integers.


Listing 23.10. The Point3D class used by the model, views, and controller.
/*
** Point3D.java
**
** An x, y, z coordinate
**
*/

public class Point3D {
    public int x, y, z;

    public Point3D(int ix, int iy, int iz) {
        x = ix;
        y = iy;
        z = iz;
    }

    public void move(int nx, int ny, int nz) {
        x = nx;
        y = ny;
        z = nz;
    }

    public void translate(int tx, int ty, int tz) {
        x += tx;
        y += ty;
        z += tz;
    }
}

The data model for this applet is shown in Listing 23.11. If Java supported multiple inheritance, PointList would inherit from Observable and from Vector. Instead, it inherits from Observable and aggregates a Vector. It would have been equally possible to inherit from Vector and aggregate an Observable with a reference back to Vector. Semantically, the two are equivalent. For this example, PointList does not define pass-through methods for all of the methods in Vector. In the addElement and setElementAt methods, you can see the update notifications being generated.

PointList does not have any intrinsic behavior other than storage. In another application, it might perform validation on the points, or provide persistent storage. Because the Observers and controller have no details about the inner workings of PointList, these features could be added without changing a single line of code in the other classes.


Listing 23.11. PointList class from the AppletCAD example.
/*
** PointList.java
**
** An Observable which includes a vector of Point3D.
**
*/

import java.util.*;
import Point3D;

public class PointList extends Observable {
    Vector _points;

    public PointList() {
        _points = new Vector(10);
    }

    public PointList(int capacity) {
        _points = new Vector(capacity);
    }

    public final Enumeration elements() {
        return _points.elements();
    }

    public final synchronized void addElement(Object obj) {
        setChanged();
        _points.addElement(obj);
        notifyObservers();
        clearChanged();
    }

    public final int indexOf(Object elem) {
        return _points.indexOf(elem);
    }

    public final synchronized void setElementAt(Object obj, int index) {
        setChanged();
        _points.setElementAt(obj, index);
        notifyObservers();
        clearChanged();
    }
}

The Observers

Although there are three Observers, each from their own class, they are sufficiently alike that we can examine just one. All three Observers are derived from a common base class. (This is not necessary in general; it happened to be convenient for this applet.) That base class, TwoDView, is shown in Listing 23.12, followed by the derived class XYView in Listing 23.13.


Listing 23.12. TwoDView, the base class for two-dimensional views in the AppletCAD example.
/*
** TwoDView.java
**
** Two dimensional view for AppletCAD example
**
*/

import java.awt.*;
import java.util.*;
import PointList;
import Point3D;

public abstract class TwoDView extends Panel implements Observer {
    protected PointList pl;
    private final int OVALWIDTH = 2;

    public void update(Observable item, Object arg) {
        if(item instanceof PointList) {
            pl = (PointList)item;

            repaint();
        }
        else
            System.out.println("Don't know how to observe that!");
    }

    public void paint(Graphics g) {
        Dimension d = size();

        super.paint(g);

        g.setColor(Color.darkGray);
        g.fill3DRect(0, 0, d.width, d.height, true);

        g.setColor(Color.white);
        drawPoints(g);
    }

    public Insets insets() {
        return new Insets(5, 5, 5, 5);
    }

    public final void drawPoints(Graphics g) {
        Point3D     vertex;
        Point       point;

        if(pl != null) {
            point = new Point(0, 0);

            for(Enumeration e = pl.elements(); e.hasMoreElements();) {
                vertex = (Point3D)e.nextElement();
                projectPoint(vertex, point);
                drawPoint(g, point);
            }
        }
    }

    protected void drawPoint(Graphics g, Point p) {
        g.fillOval(p.x - OVALWIDTH/2,
            p.y - OVALWIDTH/2,
            OVALWIDTH, OVALWIDTH);
    }

    // Sub-classes must implement this.
    protected abstract void projectPoint(Point3D v, Point p);
}

TwoDView does maintain a reference to the model-PointList. It updates this reference each time update is called. Whenever a redisplay occurs, it obtains an enumerator from PointList (which in turn obtains it from its Vector). TwoDView walks the enumerator, drawing each point. In order to draw a three-dimensional point on a two-dimensional screen, TwoDView projects the point onto two dimensions. The details of the projection are deferred to a subclass by the abstract projectPoint method. The current three subclasses each select two dimensions of the three, performing an orthogonal projection. As an exercise, you could add a fourth view to perform a perspective or orthographic projection for realistic three-dimensional visualization, simply by inheriting from TwoDView and performing the calculations in projectPoint. A completely unrelated view might display each point as coordinates. By implementing Observer, you can add a new view without affecting any other class in the applet (except for AppletCAD, of course, which would need to construct the new view).


Listing 23.13. XYView class, a concrete subclass of TwoDView.
/*
** XYView.java
**
** Two dimensional view for AppletCAD example
**
*/

import java.awt.*;
import TwoDView;
import Point3D;

public class XYView extends TwoDView {
    protected void projectPoint(Point3D v, Point p) {
        p.x = v.x;
        p.y = v.y;
    }
}

The Controller

For this example, the controller is a simple derivative of Panel, shown in Listing 23.14. In response to the Add Point button, it constructs a new Point3D with the coordinates entered in the text fields. It then asks the PointList to add the new point. Notice that the views and the controller each have a reference to the model, but the model has no direct references to any other class. This is quite typical of a model/view/controller system. The model stands independently, whereas its manipulators must know how to find the model. The model simply knows that certain Observers have expressed an interest in its state.


Listing 23.14. CoordControl class.
/*
** CoordControl.java
**
** Coordinate entry controller for AppletCAD example.
**
*/

import java.awt.*;
import java.util.*;
import PointList;
import Point3D;

public class CoordControl extends Panel {
    private static String dimension[] = {"X:", "Y:", "Z:"};

    private PointList  _model;
    private Button     _addButton;
    private TextField  _field[];

    public CoordControl(PointList pl) {
        super();

        GridBagLayout      g;
        GridBagConstraints c;
        TextField          textField;
        Label              label;
        int                i;

        // Remember the model.
        _model = pl;

        setBackground(Color.lightGray);
        setFont(new Font("Times", Font.PLAIN, 12));

        // Create the controls.  Use a GridBagLayout.
        g = new GridBagLayout();
        c = new GridBagConstraints();
        setLayout(g);
        c.fill = GridBagConstraints.HORIZONTAL;

        // Add a caption
        label = new Label("Coordinates", Label.CENTER);
        c.weightx = 0.0;
        c.gridwidth = GridBagConstraints.REMAINDER;
        g.setConstraints(label, c);
        add(label);

        // Add a label and text field for each dimension
        _field = new TextField[3];

        for(i = 0; i < 3; i++) {
            label = new Label(dimension[i], Label.RIGHT);
            c.weightx = .1;
            c.gridwidth = 1;
            g.setConstraints(label, c);
            add(label);

            _field[i] = new TextField();
            c.weightx = 1.0;
            c.gridwidth = GridBagConstraints.REMAINDER;
            g.setConstraints(_field[i], c);
            add(_field[i]);
        }

        // Add a button
        _addButton = new Button("Add Point");
        c.weightx = 0.0;
        c.gridwidth = GridBagConstraints.REMAINDER;
        g.setConstraints(_addButton, c);
        add(_addButton);
    }

    public boolean handleEvent(Event e) {
        Object target = e.target;

        if(e.id == Event.ACTION_EVENT) {
            if(target == _addButton) {
                createPoint();
            }
        }

        return super.handleEvent(e);
    }

    private final void createPoint() {
        Point3D p = new Point3D(0, 0, 0);

        p.x = Integer.parseInt(_field[0].getText());
        p.y = Integer.parseInt(_field[1].getText());
        p.z = Integer.parseInt(_field[2].getText());

        _model.addElement(p);
    }
}

HTML Container

The HTML container for this applet is shown in Listing 23.15. It just reserves some space for the applet and provides links to the applet code.


Listing 23.15. HTML container for the AppletCAD example.
<html>
<title>AppletCAD</title>
<body>
<h1>AppletCAD</h1>
<hr>
<applet code=AppletCAD.class width=300 height=400>
</applet>
<menu>
<li><a href="AppletCAD.java">AppletCAD.java</a><br>
<li><a href="PointList.java">PointList.java</a><br>
<li><a href="Point3D.java">Point3D.java</a><br>
<li><a href="CoordControl.java">CoordControl.java</a><br>
<li><a href="TwoDView.java">TwoDView.java</a><br>
<li><a href="XYView.java">XYView.java</a><br>
<li><a href="YZView.java">YZView.java</a><br>
<li><a href="XZView.java">XZView.java</a><br>
</menu>
<hr>
</html>

Interaction

The model, view, and controller classes in the AppletCAD example interact independently of the applet itself. The event diagram in Figure 23.11 should look familiar. AppletCAD is really nothing more than a concrete implementation of the MVC architecture, so the event diagram looks very similar. In this concrete example, as in the previous example, you can see that the Model, PointList, calls its own notifyObservers(). This yields adequate performance because the controller does not allow for rapid batches of updates. This class deals with the consistency problem by making all of its state-changing methods final. (This is one advantage of Java over C++ or Delphi.)

Figure 23.11: Event interaction diagram for the AppletCad example.

The chain of events begins when the user clicks the Add Point button on the controller panel. The controller knows the model, and it passes a new Point3D to the model. In response, the model notifies all interested Observers of the update. In this example, all three views derive from a common base class. They all share the implementation of update(). Observer does not require this. The update() method of a different view might store these points in an object store, or even send a message to another applet or server. Nothing limits Observers to graphical entities. The three views each query the model for its state, by enumerating the Point3Ds and drawing each point. Notice that the applet itself is missing from the event diagram.

Sometimes, instead of having the controller directly manipulate the model, you might want the controller to construct a "Command" object and pass it to a mediator. In that case, the mediator would have the reference to the model. Whether your controller directly manipulates the model or goes through an intermediary, the model has no knowledge of which controller sent a message. In fact, it has no knowledge of how many or what type of controllers exist. The model is decoupled from the controller(s), and it never needs to interact with the controller.

Note
You might ask, "What about a controller that adapts its interface according to the state of the model?" In that case, what you really have is a controller that is also a view! Remember the discussion earlier about mutually dependent classes? Clearly, it is also possible to have dependency trees or graphs! Be even more careful than usual with dependency graphs. They must be acyclic, or you risk "update storms."

Other Applications of Observer

Although most of this chapter has focused on Observer in the context of user interfaces, it has far more general applications. Observer applies whenever one object depends on another object's state. In other words, any time objects must remain consistent, think Observer. Here are a few examples outside of the GUI realm, which might spark some ideas.

For example, a simulation might require a Clock object to notify the actors on each second. By implementing the Clock as an Observable, the notification mechanism comes for free. By working at a higher, more abstract level, the simulation developers can focus on the important issues and not get bogged down in mechanics.

A point-of-sale system might use Observer to tally receipts. Whenever the cash register object rings up a sale, an inventory object needs to be notified. Call the cash register an Observable and call the inventory an Observer, and you've got it. Again, two objects need to remain synchronized. Here, the inventory object itself might also be an Observable, with an automatic ordering system object as the Observer.

Real-time data collection systems usually need some sort of data quality monitor. The monitor should raise an alert or flag data that falls outside of normal tolerances, due to bad inputs or malfunctioning hardware. The sensor input processing objects are Observables, and the monitor can be an Observer. In fact, the logging or post-processing objects can also be Observers.

The Observable pattern easily accomplishes these necessary tasks. By providing the Observer/Observable pair, Java assists you in creating extremely flexible object-oriented software. In the near future, Java will also run on cellular phones, television set-top boxes, and network computers. When that happens, the need to decouple dependent objects in Java applications will increase tenfold, as we try to support a mind-boggling variety of environments and interfaces.

Summary

Using the java.util.Observer interface and java.util.Observable class can greatly enhance your application's flexibility. By decoupling dependent classes, Observables establish a one-to-many publish-subscribe protocol. Observers are automatically notified of changes to the Observables state. Observers are often used as user interface components. The design pattern Observer captures this relationship as a "solution to a problem in a context." Design patterns provide a common vocabulary. The Observer pattern can be implemented with many variations for different situations. One such implementation is the Model-View-Controller architecture from Smalltalk-80. MVC separates the system into a Model, one or more Views, and one or more Controllers. The Model is an Observable, and the Views are Observers.

Observer has many applications beyond the GUI realm. Any time an object depends upon another object, Observer is applicable. Observers and Observables can be mutually dependent and layered. They can have dependency graphs, but cyclic graphs introduce special problems.

By using Observers in your applications, you gain several benefits, including flexibility, readability, maintainability, and robustness. Observers are more flexible because Observers can be extended, added, or replaced without changing the underlying Observable. Your code becomes more readable because the Observer pattern can easily be recognized and provides a well-known framework. Maintainability and robustness come directly from the fact that the classes are decoupled, reducing the ripple effect.