Observer Design Pattern = Publishers + Subscribers

1. What Observer Pattern defined

The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.

The following image is from book Head First Design Patterns.

Points:

  • What does this have to do with one-to-many relationship
    With the observer pattern, the Subject is the object that contains the state and controls it. So, there is ONE subject with state. The observers, on the other hand use the state, even if they don’t own it. There are many observers and they rely on the Subject to tell them when its state changes. So there is a relationship between the ONE Subject to the MANY Observers.
  • How does dependence come into this?
    Because the subject is the sole owner of the data, the observers are dependent on teh subject to update them when the data changes. This leads to a cleaner OO design than allowing many objects to control the same data.

2. Structure

The UML is shown as follows.

Points:

  1. Objects use this ISubject interface to register as observers and also to remove themselves from being observers.
  2. Each subject can have many observers.
  3. The IObserver interface just has one method update() that gets called when the subject’s state changes.
  4. A concrete subject implements the ISubject interface. Besides the register and remove method, the concrete subject implements a notify() method which is used to update all the current observers.
  5. The concrete subject may also have methods for setting and getting its state.
  6. Concrete observers can be any class that implements the IObserver interface. Each observer registers with a concrete subject to receive updates.

3. Advantages

The Observer Pattern provides an object design where subjects and observers are loosely coupled. Why?

  • The only thing the subject knows about an observer is that it implements a certain interface (the Observer interface). It doesn’t need to know the concrete class of the observer, what is does, or anything else about it.
  • We can add new observers at any time.
  • We never need to modify the subject to add new types of observers.
  • We can reuse subjects or observers independently of each other
  • Changes to either the subject or an observer will not affect the other (power of loosly coupled).

Tip about Loosely coupled

  • What: when two objects are loosely coupled, they can interact, but have very little knowledge of each other.
  • Advantage: allow us to build flexible OO systems that can handle change because they minimize the interdependency between objects.

4. Example

In this example, a weather station example will introduced. Through subscribing Weather stations in different states, the local residents can get the latest weather condition.

Step 1: Create interfaces

  • WeatherStation : the Subject interface

    public interface WeatherStation {
    
      String registerObserver(Observer observer);
    
      String removeObserver(Observer observer);
    
      void notifyObservers();
    }
  • Observer: the Observer interface

    public interface Observer {
      void update(float temp, float humi, float pres);
    }
  • Display : used to show the weather data

    public interface Display {
      void displayWeatherData ();
    }

Step 2: Concrete Subject

  • DemaciaWeatherStation : the weather station in Demacia, reports data includes temperature, humidity, pressure.
CODE
public class DemaciaWeatherStation implements WeatherStation{

    // some states reflecting the weather in demacia
    private float temperature;
    private float humidity;
    private float pressure;

    // observers
    private List<Observer> observers;

    /*
    * constructor
    * */
    public DemaciaWeatherStation() {
        this.observers = new ArrayList<>();
    }


    /*
    * set weather data
    * */
    public void setWeatherData(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }

    public void measurementsChanged () {
        notifyObservers();
    }


    @Override
    public String registerObserver(Observer observer) {
        if (observer instanceof DemaciaObserver) {
            if (this.observers.contains(observer))
                throw new RuntimeException("This observer has already been registered.");

            this.observers.add(observer);
            return "Success!";
        } else {
            throw new RuntimeException("You have registered a wrong channel.");
        }

    }

    @Override
    public String removeObserver(Observer observer) {

        if (observer instanceof DemaciaObserver) {
            if (!this.observers.contains(observer))
                throw new RuntimeException("This observer does not exist");

            this.observers.remove(observer);
            return "Success!";

        } else {

            throw new RuntimeException("Unexpected observer type.");

        }
    }

    @Override
    public void notifyObservers() {
        for (Observer observer: this.observers) {
            DemaciaObserver demaciaObserver = (DemaciaObserver) observer;
            demaciaObserver.update(this.temperature, this.humidity, this.pressure);
        }
    }
}

Step 3: Concrete Observer

  • DemaciaObserver : the observer in Demacia.
CODE
public class DemaciaObserver implements Observer, Display {

    private float temperature;
    private float humidity;
    private float pressure;

    private WeatherStation weatherStation;

    public DemaciaObserver(WeatherStation weatherStation) {
        this.weatherStation = weatherStation;
        this.weatherStation.registerObserver(this);
    }

    @Override
    public void update(float temp, float humi, float pres) {
        this.temperature = temp;
        this.humidity = humi;
        this.pressure = pres;
        displayWeatherData();
    }

    @Override
    public void displayWeatherData() {
        System.out.println("Current weather in Demacia is:");
        System.out.println("Temperature: " + this.temperature + " ; " 
            + "Humidity: " + this.humidity + " ; " 
            + "Pressure: " + this.pressure);
    }

Final Setp: Let’s test it!

CODE
public class TestObserver {

    public static void main(String[] args) {

        WeatherStation dWeatherStation = new DemaciaWeatherStation();

        DemaciaObserver Garen = new DemaciaObserver(dWeatherStation);
        DemaciaObserver Lux = new DemaciaObserver(dWeatherStation);

        ((DemaciaWeatherStation) dWeatherStation).setWeatherData(35, 2, 1);

        ((DemaciaWeatherStation) dWeatherStation).setWeatherData(40, 5, 1);
    }
}

The output is

From the above result we can see if the local subscribed the weather station, after the weather station notified them, then they could get the latest information.


   Reprint policy


《Observer Design Pattern = Publishers + Subscribers》 by Tong Shi is licensed under a Creative Commons Attribution 4.0 International License
  TOC