Back to Design Patterns
Behavioral Pattern

Observer Pattern

Define a one-to-many dependency so that when one object changes state, all its dependents are notified and updated automatically.

Intermediate10 min read

What is the Observer Pattern?

The Observer pattern is a behavioral design pattern where an object (called the Subject or Publisher) maintains a list of dependents (called Observers or Subscribers) and notifies them automatically whenever its state changes.

It establishes a one-to-many relationship between objects: one subject, many observers. When the subject changes, every observer is instantly informed without the subject needing to know anything specific about them.

This pattern is also known as the Publish-Subscribe (Pub/Sub) pattern. Think of a YouTube channel: you (observer) subscribe to a channel (subject). Whenever a new video is uploaded (state change), you get a notification — without the creator knowing who you personally are.

Key Idea: Objects subscribe to another object's events and react to changes — without tight coupling between them.

Core Participants

Subject

Subject (Publisher / Observable)

The object that holds state and triggers notifications. It maintains a list of observers, and exposes methods to register, remove, and notify them. It doesn't care who or how many observers are listening.

Observer

Observer (Subscriber / Listener)

The interface all concrete observers must implement. It typically defines a single update() method that the subject calls when notifying. This ensures all observers speak the same language.

Concrete Subject

Concrete Subject

The real implementation of the subject. It holds the actual state (e.g., stock price, user data) and calls notifyObservers() whenever that state changes.

Concrete Observer

Concrete Observer

The actual subscriber. Implements the Observer interface and defines its own reaction to a notification — e.g., sending an email, updating a UI element, writing a log entry.

!Problem It Solves

Tight Coupling Problem

Without the Observer pattern, if Object A changes and Object B, C, and D need to know about it, Object A has to call each of them directly. This creates hard dependencies — every new consumer means modifying the source object.

  • → Adding a new reaction means editing the subject's source code
  • → Subject becomes bloated knowing about every dependent
  • → Removing a consumer requires editing the subject
  • → Difficult to test in isolation

Example Without Observer

// Problem: Subject knows about every consumer (tight coupling)
class StockMarket {
    void setPrice(double price) {
        this.price = price;

        // Subject must call EVERY dependent manually
        mobileApp.onPriceChange(price);      // Coupled
        emailSystem.sendAlert(price);        // Coupled
        dashboardUI.refresh(price);          // Coupled
        analyticsService.track(price);       // Coupled
        // Adding new consumer = edit this class!
    }
}

Solution With Observer

// Solution: Subject only knows about the Observer interface
class StockMarket {
    void setPrice(double price) {
        this.price = price;
        notifyObservers();  // That's it — loosely coupled!
    }
    // Adding a new consumer = zero changes to this class
}

How It Works — Step by Step

1

Define the Observer Interface

All observers implement a common interface with an update() method so the subject can call them uniformly.

2

Subject Maintains a List

The subject holds a list of observer references. It exposes subscribe() and unsubscribe() methods.

3

State Changes Trigger Notification

When the subject's state changes, it loops through the list and calls update() on each registered observer.

4

Observers React Independently

Each observer handles the notification in its own way — send email, update UI, log data — completely decoupled from each other.

Notification Flow

State Change
Subject.notify()
Observer A → update()
Observer B → update()
Observer C → update()

Push vs Pull Model

There are two common ways for the subject to communicate data to its observers:

Push Model

The subject sends all relevant data directly in the update() call. Observers receive everything they need upfront.

// Subject pushes data
observer.update(stockName, price, volume);

✓ Simple and fast for observers

✗ Subject must know what data observers need

Pull Model

The subject sends a reference to itself. Observers pull whatever data they specifically need.

// Subject passes itself
observer.update(this);
// Observer pulls what it needs
double price = subject.getPrice();

✓ Flexible — observers take only what they need

✗ Observers are coupled to subject's interface

Implementation

// Observer Pattern — Stock Price Notifier
import java.util.ArrayList;
import java.util.List;

// Observer interface
interface StockObserver {
    void update(String stockName, double price);
}

// Subject interface
interface StockSubject {
    void registerObserver(StockObserver observer);
    void removeObserver(StockObserver observer);
    void notifyObservers();
}

// Concrete Subject
public class StockMarket implements StockSubject {
    private List<StockObserver> observers = new ArrayList<>();
    private String stockName;
    private double stockPrice;

    public StockMarket(String stockName) {
        this.stockName = stockName;
    }

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

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

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

    // When price changes, notify all observers
    public void setStockPrice(double price) {
        this.stockPrice = price;
        notifyObservers();
    }
}

// Concrete Observers
class MobileApp implements StockObserver {
    private String appName;

    public MobileApp(String appName) {
        this.appName = appName;
    }

    @Override
    public void update(String stockName, double price) {
        System.out.println("[" + appName + "] Push notification: "
            + stockName + " is now ₹" + price);
    }
}

class EmailAlertSystem implements StockObserver {
    @Override
    public void update(String stockName, double price) {
        System.out.println("[Email] Alert sent: "
            + stockName + " price updated to ₹" + price);
    }
}

// Usage Example
public class Application {
    public static void main(String[] args) {
        StockMarket tataStock = new StockMarket("TATA");

        StockObserver app1 = new MobileApp("Zerodha");
        StockObserver app2 = new MobileApp("Groww");
        StockObserver emailAlert = new EmailAlertSystem();

        tataStock.registerObserver(app1);
        tataStock.registerObserver(app2);
        tataStock.registerObserver(emailAlert);

        tataStock.setStockPrice(3450.75);
        // Output:
        // [Zerodha] Push notification: TATA is now ₹3450.75
        // [Groww]   Push notification: TATA is now ₹3450.75
        // [Email]   Alert sent: TATA price updated to ₹3450.75

        // Remove an observer
        tataStock.removeObserver(emailAlert);
        tataStock.setStockPrice(3480.00);
        // Only Zerodha and Groww are notified now
    }
}

When to Use Observer

Event-Driven UI Systems

When UI components need to react to data changes — like re-rendering a chart when underlying data updates.

React state, DOM events, Redux

Notification & Alerting Systems

When multiple services need to be notified about a change — like email, SMS, and push notifications all triggered by one event.

EmailAlert, SMSService, PushNotification

Model-View Synchronization

In MVC architecture, the View observes the Model. When the Model updates, all Views automatically re-render.

MVC, MVVM, MVP patterns

Logging & Monitoring

When various monitoring tools need to track system events without being embedded in business logic.

AuditLogger, MetricsCollector, DebugTracer

Real-Time Data Feeds

Live stock prices, sports scores, chat messages — anything where multiple consumers need the same live updates.

StockTicker, LiveScoreboard, ChatRoom

Pros & Cons

Advantages

  • Open/Closed Principle — add new observers without changing the subject
  • Loose coupling between subject and observers
  • Supports broadcasting to multiple objects at once
  • Dynamic subscription — observers can join/leave at runtime
  • Each observer can have independent logic

Disadvantages

  • Unexpected updates — observers may be triggered in unpredictable order
  • Memory leaks if observers forget to unsubscribe
  • Cascade of updates can be hard to debug
  • Performance overhead with many observers
  • Subject doesn't know if observers acted on the notification

Observer vs Pub/Sub — What's the Difference?

AspectObserverPub/Sub
CouplingSubject knows about observersPublisher and subscriber don't know each other
MediatorNo middle layerMessage broker / event bus in between
SynchronyUsually synchronousOften asynchronous
ScopeSingle applicationCan span multiple systems
ExampleJava Swing, DOM eventsKafka, RabbitMQ, Redis Pub/Sub

Note: Pub/Sub is a more decoupled evolution of the Observer pattern. Observer is the pattern; Pub/Sub is an architecture style based on the same idea but with a message broker separating publishers and subscribers.

Real-World Examples

React / Vue State (JavaScript)

React's useState and Vue's reactivity system are implementations of Observer — when state changes, all components subscribed to that state automatically re-render.

Java's java.util.Observable (Java)

Java's standard library has built-in Observable class and Observer interface. Android's LiveData and RxJava are built on the same concept.

Django Signals (Python)

Django's signal system (post_save, pre_delete) is a textbook Observer implementation — models broadcast signals; any registered receiver reacts to them.

Redux / Zustand (JavaScript)

Redux's store.subscribe() directly uses the Observer pattern. Every connected component is an observer of the global store. Zustand follows the same model with a simpler API.

DOM Events (Browser)

element.addEventListener('click', handler) — you're subscribing an observer (handler) to a subject (DOM element). Multiple handlers can observe the same event independently.

What's Next?

Now that you understand Observer, explore related Behavioral patterns: