1. Overview

In this tutorial, we'll investigate Java's built-in support for the observer pattern. When we try to implement the observer pattern, we generally create our Observer and Subject interfaces from scratch. However, Java also provides some classes: java.util.Observer interface for observers and java.util.Observable class for subjects.

To create observers, we need to create classes that implement java.util.Observer interface. On the other hand, to create subjects, we need to extend java.util.Observable class.

2. Implement an Observer

Let's start with the Observer interface.

Observer is defined in JDK as follows:

public interface Observer {
    /**
     * This method is called whenever the observed object is changed. An
     * application calls an <tt>Observable</tt> object's
     * <code>notifyObservers</code> method to have all the object's
     * observers notified of the change.
     *
     * @param   o     the observable object.
     * @param   arg   an argument passed to the <code>notifyObservers</code>
     *                 method.
     */
    void update(Observable o, Object arg);
}

The first parameter is the Observable object which notifies the observer in the first place. The second parameter is the observation object.

If we pass null for the second argument, communication takes place in Pull style. Thus observers pull data from the observable.

On the other hand, if we pass a not-null value for the second argument, communication takes place in Push style. Thus observable pushes data to the observers.

To illustrate the issue, we have the ForecastDisplay class:

public class ForecastDisplay implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        if (arg != null) { // PUSH Style
            System.out.println(arg);
        } else { // PULL Style
            if (o instanceof WeatherStation) {
                WeatherStation w = (WeatherStation) o;
                System.out.printf("%f\t%f\t%f%n", w.getTemp(), w.getHumidity(), w.getPressure());
            }
        }
    }
}

This is one of the Observer implementations. Furthermore, it supports both Push and Pull style communications.

3. Implement an Observable

Next, we'll see how we can implement an Observable.

We have WeatherStation which extends java.util.Observable:

public class WeatherStation extends Observable {

    private float temp;
    private float humidity;
    private float pressure;

    // Getters...

    public void measurementsChanged() {
        notifyObservers();
    }

    public void setMeasurements(float temp, float humidity, float pressure) {
        this.temp = temp;
        this.humidity = humidity;
        this.pressure = pressure;
        setChanged();
        measurementsChanged();
    }
}

Here, whenever measurements are modified, WeatherStation invokes the setChanged() method. Note that this method is inherited from the Observable class. Then it starts notifying registered observers in pull style since it isn't passing any object to the notifyObservers() method.

4. Invocation

Finally, we'll see how we can set up observers and observables:

public class ClientMain {

    public static void main(String[] args) {
        WeatherStation weatherStation = new WeatherStation();
        CurrentConditions currentConditions = new CurrentConditions();
        ForecastDisplay forecastDisplay = new ForecastDisplay();
        StatisticsDisplay statisticsDisplay = new StatisticsDisplay();
        weatherStation.addObserver(currentConditions);
        weatherStation.addObserver(forecastDisplay);
        weatherStation.addObserver(statisticsDisplay);

        Random random = new Random();
        for (int i = 0; i < 3; i++) {
            weatherStation.setMeasurements(random.nextFloat(), random.nextFloat(), random.nextFloat());
            System.out.println("***************************************");
        }
    }
}

Here, we're creating the observable WeatherStation. Then we're registering different observers like currentConditions, forecastDisplay, etc.

After this setup we can see that publish/subscribe pattern just works:

0.277968	0.903981	0.376809
0.277968	0.903981	0.376809
0.277968	0.903981	0.376809
***************************************
0.499265	0.026102	0.526112
0.499265	0.026102	0.526112
0.499265	0.026102	0.526112
***************************************
0.939757	0.075239	0.864402
0.939757	0.075239	0.864402
0.939757	0.075239	0.864402
***************************************

5. Summary

In this tutorial, we've looked at Java's support for observer pattern. More specifically, we've worked with the java.util.Observer and java.util.Observable classes.

As always, the source code is available on Github.