Skip to main content

Observer Design Pattern

Problem​

You: Hi Shubh, I read HTTP Polling and Server-Sent Events chapters in System Design Course of yours. I believe SSE would be suitable for my use case.

Note

I would recommend to not read further as of now, as this requires you to know HTTP Polling and Server-Sent Events. The final version of HTTP Polling and Server-Sent Events will be added.

Shubh: Glad that you liked my content! What are you building?

You: I am building a stock market platform where users will be notified about stock price changes if they subscribe to specific stocks.

Shubh: Yes, in that case, SSE is a good option.

You: Is there a design pattern that aligns with SSE implementation?

Shubh: The Observer Design Pattern is the right choice. Think of it this way: there would be an Observable (e.g., the stock market) and Observers (e.g., individual users like you and me). Observers can subscribe to the Observable, and when there is an update, the Observable sends notifications to all subscribed Observers. In other words, we subscribe to the stock market for price changes, and the stock market sends us updates whenever there is a change. Let me show you a UML diagram to make things clearer.

UML​

Observer Design Pattern

UML for Observer Design Pattern

  • IObserver and IObservable are interfaces defining the contract for observers and observables, respectively.

  • The key relationship here is hasA: the IObservable maintains a list of IObserver objects.

  • Whenever there is a change in the IObservable (e.g., stock price update), it calls the notify() method. This method loops through the list of IObserver instances and calls their update() method to reflect the change. This ensures all observers are updated with the latest data.

Code Implementation​

StockMarket Interface (Observable)​

StockMarket.java
public interface StockMarket {
void addObserver(StockObserver observer);
void removeObserver(StockObserver observer);
void notifyObservers(String stockName, double amount);
}

StockMarketImpl Class (Concrete Observable)​

StockMarketImpl.java
import java.util.ArrayList;
import java.util.List;

public class StockMarketImpl implements StockMarket {
private List<StockObserver> observers = new ArrayList<>();

@Override
public void addObserver(StockObserver observer) {
observers.add(observer);
}

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

@Override
public void notifyObservers(String stockName, double amount) {
for (StockObserver observer : observers) {
observer.update(stockName, amount);
}
}

// If the stock changes then notify all subscribers
public void setStock(String stockName, double amount) {
notifyObservers(stockName, amount);
}
}

StockObserver Interface (Observer)​

StockObserver.java
public interface StockObserver {
void update(String stockName, double amount);
}

Investor Class (Concrete Observer)​

Investor.java
public class Investor implements StockObserver {
private String name;

Investor(String name) {
this.name = name;
}

@Override
public void update(String stockName, double amount) {
System.out.println("Hi " + name + ", the price for " + stockName + " is now " + amount);
}
}

Main Class (Client Code)​

Main.java
public class Main {
public static void main(String[] args) {
StockMarketImpl stockMarket = new StockMarketImpl();

StockObserver investor1 = new Investor("Shubh");
StockObserver investor2 = new Investor("Kunal");

stockMarket.addObserver(investor1);
stockMarket.addObserver(investor2);

// Notify all investors about stock change
stockMarket.setStock("INFY", 200.0);

// Removing an observer
stockMarket.removeObserver(investor2);

// Notify remaining investors about another stock change
stockMarket.setStock("TCS", 500.0);
}
}

Key Benefits of the Observer Design Pattern​

Before watching, make sure you give it a try
  1. Decoupling: The Observer Pattern allows for a loosely coupled interaction between the Observable (subject) and Observers (listeners). The Observable only knows that it has a list of observers to notify and does not need to know specific details about them.

  2. Dynamic Relationships: Observers can be added or removed at runtime, allowing for a dynamic and flexible system.

  3. Scalability: Any number of observers can be attached to a subject. This makes it easy to extend the system without modifying existing code.

  4. Consistency: All subscribed observers receive the same updates simultaneously, ensuring consistent state across the system.

  5. Easy to Implement: The Observer Design Pattern is straightforward to implement and fits naturally in many event-driven or notification-based systems.