Definition

The Observer Design 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.

Introduction

In our example, we have an object that encapsulates the Stock data. It has the name of the stock symbol and the stock price.

There are a number of dependents who depend on this object. Whenever the stock data changes, they have to be notified of the change. We also have to pass the new values to them. We can think of display boards as the ones who have to be notified. Let us assume we have a couple of them here - CurrentStockDetailsDisplay and StatisticsStockDetailsDisplay. They both have an update method to get such updates.

public class StockData {
    private String stockSymbol;
    private String stockPrice;

    public void stockPriceChanged() {
        currentStockDetailsDisplay.update(stockSymbol, stockPrice);
        statisticsStockDetailsDisplay.update(stockSymbol, stockPrice);
    }
    public String getStockSymbol() {
        return stockSymbol;
    }

    public String getStockPrice() {
        return stockPrice;
    }
}
public class CurrentStockDetailsDisplay {
    public void update(String stockSymbol, String stockPrice) {
        System.out.println("Got an update for " + stockSymbol);
        display(stockSymbol, stockPrice);
    }
    
    public void display(String stockSymbol, String stockPrice) {
        System.out.println("Stock Symbol - " + stockSymbol + "; " + "Stock price - " + stockPrice);
    }
}

Problems with the above approach

The StockData class is closely coupled to the dependent objects ( CurrentStockDetailsDisplay and StatisticsStockDetailsDisplay). When we need to add a new such object (or remove an existing dependent), the we have to change the StockData class.

Observer pattern to the rescue.

The Observer pattern

In the above example, the StockData object is the Subject. The display board objects that are interested in the values of the StockData are the Observers.

The Observer Pattern defines a one-to-many dependency between the subject and the observer(s). It provides this without coupling the subject and the observers.

Implementing the Observer pattern

We will create two interfaces - Subject and Observer.

The Subject has methods for registering a new observer, removing a subscribed observer. It has a special method for notifying its observers.

The Observer interface has one method to receive updates from its subject.

In addition to this, we will also create a new interface, DisplayElement, so that all our observers can have a common interface. It has a single method called display.

public interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}
public interface Observer {
    void update(String stockSymbol, String stockPrice);
}
public interface DisplayElement {
    void display();
}
import java.util.ArrayList;
import java.util.List;

public class StockData implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String stockSymbol;
    private String stockPrice;

    public void stockPriceChanged() {
        notifyObservers();
    }
    public String getStockSymbol() {
        return stockSymbol;
    }

    public String getStockPrice() {
        return stockPrice;
    }

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        observers.forEach(observer -> observer.update(stockSymbol, stockPrice));
    }

    public void setNewStockPrice(String stockSymbol, String stockPrice) {
        this.stockSymbol = stockSymbol;
        this.stockPrice = stockPrice;
        stockPriceChanged();
    }
}
public class CurrentStockDetailsDisplay implements Observer, DisplayElement {
    private final Subject subject;
    private String stockSymbol;
    private String stockPrice;

    public CurrentStockDetailsDisplay(Subject subject) {
        this.subject = subject;
        this.subject.registerObserver(this);
    }

    @Override
    public void display() {
        System.out.println("Stock Symbol - " + stockSymbol + " " + "Stock price - " + stockPrice);
    }

    @Override
    public void update(String stockSymbol, String stockPrice) {
        this.stockSymbol = stockSymbol;
        this.stockPrice = stockPrice;
        display();
    }
}

The StockData class now has a method called setNewStockPrice. We call that method externally when the stock details change (we don’t care how). When it is called, we set the new details by updating the instance variables and call the stockPriceChanged method. Earlier it was calling each of the dependents (observers). Now, it iterates through the observers list and updates each one passing the new value.

public class Main {
    public static void main(String[] args) {
        StockData stockData = new StockData();
        CurrentStockDetailsDisplay currentStockDetailsDisplay = new CurrentStockDetailsDisplay(stockData);
        stockData.setNewStockPrice("StockA", "10.5");
        stockData.setNewStockPrice("StockA", "11.5");
    }
}

Outputs:

Stock Symbol - StockA Stock price - 10.5
Stock Symbol - StockA Stock price - 11.5

Structure

ObserverPattern

Participants

Subject:

  • It knows its observers. Any number of Observer objects may observe a subject.
  • Provides an interface for attaching and detaching Observer objects.

Observer:

  • Defines an updating interface for objects that should be notified of changes in a subject.

ConcreteSubject:

  • Stores state of interest to ConcreteObserver objects.
  • Sends a notification to its observers when its state changes.

ConcreteObserver:

  • Maintains a reference to a ConcreteSubject object.
  • Stores state that should stay consistent with the subject’s.
  • Implements the Observer updating interface to keep its state consistent with the subject’s.

Design principles used

Strive for loosely coupled designs between objects that interact.

When two objects are loosely coupled, they can interact, but have very little knowledge of each other. The Observer pattern provides an object design where subjects and observers are loosely coupled.

Conclusion

In this post, we looked at the Observer Design pattern. It is an useful pattern when we have a lot of observers or dependant objects that are interested in the state of a central object or the subject. It helps us have the subject notify all the interested observers without coupling them. In this post, we saw how the Subject pushes the state values to the observers.

The RxJava library’s Observable works based on this pattern. Check out the Introduction to RxJava and RxJava Observable to learn more.

References

  1. Head First Design Patterns: A Brain-Friendly Guide by Eric Freeman and Elisabeth Robson.
  2. Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides.
  3. Observer_pattern – Wikipedia