Observer pattern

Problem statement

Imagine that you have two types of objects: a Customer and a Store. The customer is very interested in a particular brand of product which should become available in the store very soon.

The customer could visit the store every day and check product availability. But while the product is still en route, most of these trips would be pointless.

On the other hand, the store could send tons of emails (which might be considered spam) to all customers each time a new product becomes available. This would save some customers from endless trips to the store. At the same time, it’d upset other customers who aren’t interested in new products.

It looks like we’ve got a conflict. Either the customer wastes time checking product availability or the store wastes resources notifying the wrong customers.

The solution

The object that has some interesting state is often called subject, but since it’s also going to notify other objects about the changes to its state, we’ll call it publisher.

All other objects that want to track changes to the publisher ’s state are observers. We call them subscribers.

Features

  1. Don’t miss out when something interesting happens.
  2. Keep your objects in the know when something they might care about happens.
  3. Objects can decide at runtime whether they want to be kept informed.
  4. The Observer Pattern is one of the most heavily used pattern in the JDK and it is incredibly useful.

Participants

Publishers + Subscribers = Observer pattern. Only, we call the publisher the SUBJECT and the subscribers the OBSERVERS.

  1. Subject (Subject interface): Provides an interface to attach and detach Observer objects.
  2. ConcreteSubject (Product class): Implements the Subject interface. A ConcreteSubject sends notification to Observer objects when its state change.
  3. Observer (Observer interface): Provides an interface for objects that should be notified of changes in a Subject.
  4. ConcreteObserver (Bidder class): Implements Observer to receive notifications from the Subject and keep its state consistent with the state of the Subject.

One-to-many relationship

The Subject is the object that contains the state and controls it. So there is ONE subject with state. The observers 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 and the MANY observers.

Design principle: Strive for loosely coupled designs between objects that interact

Definition

Observer is a behavioral design pattern that lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they’re observing.

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 subject and observers define the one-to-many relationship. The observers are dependent on the subject such that when the subject’s state changes, the observers get notified. Depending on the style of notification, the observer may also be updated with new values.

There are a few different ways to implement the Observer Pattern, but most revolve around a class design that includes Subject and Object interfaces.

  1. The Subject interface - Objects use this interface to register as observers and also to remove themselves from being observers.
  2. Concrete Subject classes always implement the Subject interface. In addition to the register and remove methods, the concrete subject implements a notifyObservers() method that is used to update all the current observers whenever the state changes.
  3. The Observer interface - All potential observers need to implement this interface. It has a method called update() that gets called when the Subject’s state changes.

Use cases

Weather broadcasting

Create a broadcast class. It keeps track of all the objects listening to it, and anytime a new piece of data comes along, it sends a message to each listener. Whats cool is that the listeners can join the broadcast at any time or they can even remove themselves. It is really dynamic and loosely coupled.

Newspaper or magazine subscriptions

  1. A newspaper publisher goes into business and begins publishing newspapers.
  2. You subscribe to a particular publisher, and every time there is a new edition, it gets delivered to you. As long as you remain a subscriber, you get new newspapers.
  3. You unsubscribe when you don’t want papers anymore, and they stop being delivered to you.
  4. While the publisher remains in business, people, hotels, airlines, and other businesses constantly subscribe and unsubscribe to the newspaper.

Statement of work

The weather statement will be based on WeatherData object, which tracks current weather conditions (temperature, humidity, and barometric pressure). Create an application that initially provides three display elements: current conditions, weather statistics, and a simple forecast, all updated in real time as the WeatherData object acquires the most recent measurements.

Further, this is an expandable weather station. We want to release an API so that other developers can write their own weather displays and plug them right in.

public class WeatherData {

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

        public void measurementsChanged() {
                float temperature = getTemperature();
                float humidity = getHumidity();
                float pressure = getPressure();

                currentConditionsDisplay.update(temperature, humidity, pressure);
                statisticsDisplay.update(temperature, humidity, pressure);
                forecastDisplay.update(temperature, humidity, pressure);
        }

        // Grab the most recent measurements by calling the WeatherData's getter methods.

        public float getTemperature() {
                return temperature;
        }

        public float getHumidity() {
                return humidity;
        }

        public float getPressure() {
                return pressure;
        }

        // Other helpful methods
}

What is wrong with this implementation?

  1. currentConditionsDisplay.update
  2. statisticsDisplay.update
  3. forecastDisplay.update

By coding to concrete implementations, we have no way to add or remove other display elements without making changes to the program. This is an area of change. We need to encapsulate this.

Implementation

The observer interface:

public interface Observer {
     public void update(float temp, float humidity, float pressure);
}

The subject interface:

 public interface Subject {
     public void registerObserver(Observer o);
     public void removeObserver(Observer o);
     public void notifyObservers();
}

The display interface:

public interface DisplayElement {
     public void display();
}

The concrete subject class:

import java.util.ArrayList;

public class WeatherData implements Subject {

    private ArrayList<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData() {
            this.observers = new ArrayList<Observer>();
    }

    public void registerObserver(Observer o) {
            observers.add(o);
    }

    public void removeObserver(Observer o) {
            int i = observers.indexOf(o);
            if (i >= 0) {
                    observers.remove(i);
            }
    }

    public void notifyObservers() {
            for (Observer o : observers) {
                    o.update(temperature, humidity, pressure);
            }
    }

    public void measurementsChanged() {
            notifyObservers();
    }
}

The display concrete class:

 public class CurrentConditionDisplay implements Observer, DisplayElement {

     private float temperature;
     private float humidity;
     private Subject weatherData;

     public CurrentConditionDisplay(Subject weatherData) {
             this.weatherData = weatherData;

             // Why are we storing a reference to the Subject?
             // In the future, we may want to un-register ourselves as an observer and it
             // would be handy to already have a reference to the subject.
             weatherData.registerObserver(this);
     }

     public void update(float temp, float humidity, float pressure) {
             this.temperature = temp;
             this.humidity = humidity;
             display();
     }

     public void display() {
             System.out.println("Current conditions: " + temperature + " F degrees and " + humidity + " % humidity");
     }

}

Change in requirement:

We want to display a Heat Index display element. It is an index that combines temperature and humidity to determine the apparent temperature (how hot it actually feels). The formula will be given to you.

HeatIndexDisplay.java

public class HeatIndexDisplay implements Observer, DisplayElement {
     float heatIndex = 0.0f;
     private WeatherData weatherData;

     public HeatIndexDisplay(WeatherData weatherData) {
             this.weatherData = weatherData;
             weatherData.registerObserver(this);
     }

     public void update(float t, float rh, float pressure) {
             heatIndex = computeHeatIndex(t, rh);
             display();
     }

     private float computeHeatIndex(float t, float rh) {
             float index = (float) ((16.923 + (0.185212 * t) + (5.37941 * rh) - (0.100254 * t * rh) + (0.00941695 * (t * t))
                             + (0.00728898 * (rh * rh)) + (0.000345372 * (t * t * rh)) - (0.000814971 * (t * rh * rh))
                             + (0.0000102102 * (t * t * rh * rh)) - (0.000038646 * (t * t * t)) + (0.0000291583 * (rh * rh * rh))
                             + (0.00000142721 * (t * t * t * rh)) + (0.000000197483 * (t * rh * rh * rh))
                             - (0.0000000218429 * (t * t * t * rh * rh)) + 0.000000000843296 * (t * t * rh * rh * rh))
                             - (0.0000000000481975 * (t * t * t * rh * rh * rh)));
             return index;
     }

     public void display() {
             System.out.println("Heat index is " + heatIndex);
     }
}

Push vs Pull

Why don’t we write some public getter methods that will let us pull out the state we need?

Yes, we can pull the subject’s state. But, won’t that be less convenient for the observers? If the observers have to come to the subject every time they want something, the observers might have to make multiple method calls to get all the state they want. Instead, if the subject pushes the state, the observers will get everything they need in one notification.

But there are so many different kinds of observers. There is no way the subject can anticipate everything the observers need. Sometimes, the subjects just have to let the observers come to them to get the state the observers need. That way, if some of the observers need only a little bit of state, they aren’t forced to get it all. It also makes things easier to modify later. Say, for example, if the subject expands and adds more state to itself, if the observers are using pull, the observers don’t have to go around and change the update calls on every observer. The subject just needs to change to allow more getter methods to access the newly added addtitional state.

There are advantages to doing it both ways.

The built-in Java Observer pattern allows you to use either push or pull.

Java’s built-in Observer Pattern

Java has built-it support for Observer Pattern in several of its APIs. The most general is the (now deprecated) Observer interface and the Observable class in the java.util package. They give a lot of functionality out of the box.

How does Java’s built-in pattern work?

For the Observable to send notifications:

You need to be Observable by extending the java.util.Observable superclass. From there, it is a two-step process.

  1. First, you must call the setChanged() method to signify that the state has changed in your object.

  2. Second, call one of two notifyObservers() methods

    1. notifyObservers() or
    2. notifyObservers(Object args)

    If notifyObservers() is called without first calling setChanged(), the observers will not be notified.

For an Object to become an Observer:

Implement the java.util.Observer interface and call addObserver() on any Observable object. Likewise, to remove yourself as observer, just call deleteObserver()

For an Observer to receive notifications:

It implements the update() method. The signature of the method is update(Observable o, Object arg) What is o and what is arg? o is the Subject that sent the notification arg is the data object that was passed to notifyObservers(), or null if a data object wasn’t specified.

If data has to be pushed to the observers, it can be passed as a data object to notifyObservers(arg). If not, then the Observer has to “pull” the data it wants from the Observable object passed to it. How? Lets see below.

Reworking the Weather Station with the built-in support

WeatherData:

import java.util.Observable;

// sub-classing Observable
public class WeatherData extends Observable {
        private float temperature;
        private float humidity;
        private float pressure;

        // Constructor no longer needs to create a data structure to hold Observers.
        public WeatherData() { }

        public void measurementsChanged() {
                setChanged();
                notifyObservers();
        }

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

        // Used in the "pull" mechanism
        public float getTemperature() {
                return temperature;
        }

        // Used in the "pull" mechanism
        public float getHumidity() {
                return humidity;
        }

        // Used in the "pull" mechanism
        public float getPressure() {
                return pressure;
        }
}

CurrentConditionsDisplay:

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

// Implement the Observer interface
public class CurrentConditionsDisplay implements Observer, DisplayElement {
        Observable observable;
        private float temperature;
        private float humidity;

        // Constructor takes an Observable and we use this to add CurrentConditionsDisplay as an Observer
        public CurrentConditionsDisplay(Observable observable) {
                this.observable = observable;
                observable.addObserver(this);
        }

        // This takes both an Observable and the optional data argument
        public void update(Observable obs, Object arg) {
                // Make sure the Observable is of type WeatherData and then use it's getter methods to get data.
                if (obs instanceof WeatherData) {
                        WeatherData weatherData = (WeatherData)obs;
                        this.temperature = weatherData.getTemperature();
                        this.humidity = weatherData.getHumidity();
                        display();
                }
        }

        public void display() {
                System.out.println("Current conditions: " + temperature
                        + "F degrees and " + humidity + "% humidity");
        }
}

Never depend on order of evaluation of the Observer notifications

If your code/application depends on a specific notification order, you need to be very careful with using this pattern. If you change Observable/Observer implementations, the order of the notifications could change and your application would produce incorrect results. It is definitely not considered a loosely-coupled solution.

Issues with Java’s built-in implementation

TODO

Page 71

Other places you will find Observer pattern in the JDK

The java.util implementation of the Observer/Observable pattern is deprecated. Both JavaBeans and Swing also provide their own implementations of the pattern.

TODO

Look at the implementation in JavaBeans package

Observer Pattern in the Spring Framework

In Spring, the Observer Pattern is used in the ApplicationContext’s event mechanism. The ApplicationEvent class and ApplicationListener interface of Spring enables event handling in Spring ApplicationContext. When you deploy a bean that implements the ApplicationListener interface, it will receive an ApplicationEvent every time the ApplicationEvent is published by an event publisher. Here, the event publisher is the subject and the bean that implements ApplicationListener is the observer.

If you are creating your own custom event, your event publisher (subject) must implement the ApplicationEventPublisherAware interface. This interface has a setter method named setApplicationEventPublisher() that provides an ApplicationEventPublisher object for using in your class. The subject can then publish an event by calling the publishEvent() method of ApplicationEventPublisher. The subject can publish any event that extends ApplicationEvent and when the subject does so, the bean implementing ApplicationListener (observer) receives the event.

Alternatives

KNOWLEDGE GAP - LEARN MORE

There are many alternatives of Observer design pattern and Reactive Streams is one of them.

Reactive Streams or Flow API:

Flow is a class introduced in Java 9 and has 4 interrelated interfaces : Processor, Publisher, Subscriber and Subscription.

Flow.Processor : A component that acts as both a Subscriber and Publisher.

Flow.Publisher : A producer of items received by Subscribers.

Flow.Subscriber : A receiver of messages.

Flow.Subscription: Message control linking a Flow.Publisher and Flow.Subscriber.

See Java doc https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/Flow.html

Implementation

https://github.com/explorer436/programming-playground/tree/main/java-playground/design-pattern-samples/design-pattern-examples

External references

  1. https://dzone.com/articles/javas-observer-and-observable-are-deprecated-in-jd
  2. https://gist.github.com/mtorchiano/e69ac7e309fee81bd17f4f0740b9ffa9
  3. https://stackoverflow.com/questions/46380073/observer-is-deprecated-in-java-9-what-should-we-use-instead-of-it
  4. https://stackoverflow.com/questions/11619680/why-should-the-observer-pattern-be-deprecated/11632412#11632412
  5. https://springframework.guru/gang-of-four-design-patterns/observer-pattern/