Home/Learn/Low Level Design/Observer Pattern

Observer Pattern

Beginner
Behavioral Patterns

Defines a one-to-many dependency so that when one object (Subject) changes state, all its dependents (Observers) are notified automatically.

Overview

The Observer pattern implements a publish-subscribe mechanism. The Subject maintains a list of Observer references and calls update() on each when its state changes. In Java, this is embodied by java.util.Observable (deprecated in Java 9 — use EventListener pattern or Spring's ApplicationEvent instead), PropertyChangeListener, and Swing event listeners. Reactive frameworks (RxJava, Project Reactor) extend Observer to support backpressure and error handling. Key trade-off: loose coupling between Subject and Observer vs potential memory leaks (Observer not removed when no longer needed — "lapsed listener" problem).

Observer Implementation

Observer interface has an update() method. Subject maintains a list of Observers and provides register/deregister methods. ConcreteSubject stores state and notifies all observers on change.

Java — Observer (Stock Ticker)
import java.util.ArrayList;
import java.util.List;

// Observer interface
public interface StockObserver {
    void onPriceChange(String symbol, double newPrice, double oldPrice);
}

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

// Concrete Subject
public class StockTicker implements StockSubject {
    private final String symbol;
    private double currentPrice;
    private final List<StockObserver> observers = new ArrayList<>();

    public StockTicker(String symbol, double initialPrice) {
        this.symbol       = symbol;
        this.currentPrice = initialPrice;
    }

    @Override public void addObserver(StockObserver o)    { observers.add(o); }
    @Override public void removeObserver(StockObserver o) { observers.remove(o); }

    public void setPrice(double newPrice) {
        double oldPrice   = this.currentPrice;
        this.currentPrice = newPrice;
        notifyObservers(oldPrice); // state changed — notify
    }

    private void notifyObservers(double oldPrice) {
        observers.forEach(o -> o.onPriceChange(symbol, currentPrice, oldPrice));
    }

    @Override public void notifyObservers() {
        observers.forEach(o -> o.onPriceChange(symbol, currentPrice, currentPrice));
    }
}

// Concrete Observers
public class AlertObserver implements StockObserver {
    private final double threshold;
    public AlertObserver(double threshold) { this.threshold = threshold; }

    @Override
    public void onPriceChange(String symbol, double newPrice, double oldPrice) {
        if (Math.abs(newPrice - oldPrice) / oldPrice > threshold) {
            System.out.printf("ALERT: %s moved by >%.0f%% → ₹%.2f%n",
                symbol, threshold * 100, newPrice);
        }
    }
}

public class PortfolioObserver implements StockObserver {
    private final int quantity;
    public PortfolioObserver(int quantity) { this.quantity = quantity; }

    @Override
    public void onPriceChange(String symbol, double newPrice, double oldPrice) {
        double pnl = (newPrice - oldPrice) * quantity;
        System.out.printf("Portfolio P&L for %s: %+.2f%n", symbol, pnl);
    }
}

// Usage
StockTicker infosys = new StockTicker("INFY", 1500.0);
infosys.addObserver(new AlertObserver(0.05));   // alert on 5% move
infosys.addObserver(new PortfolioObserver(100)); // holds 100 shares

infosys.setPrice(1600.0); // triggers both observers
infosys.setPrice(1580.0); // triggers both observers

Spring ApplicationEvent (Production Observer)

Spring's event system is the production-grade Observer. Publish events with ApplicationEventPublisher; handle with @EventListener. Events are synchronous by default; use @Async for background processing.

Java — Spring ApplicationEvent (decoupled Observer)
// Event (value object)
public record OrderPlacedEvent(String orderId, String userId, double amount) {}

// Publisher (Subject)
@Service
public class OrderService {
    private final ApplicationEventPublisher eventPublisher;

    public OrderService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    @Transactional
    public Order placeOrder(OrderRequest request) {
        Order order = orderRepository.save(Order.from(request));
        eventPublisher.publishEvent(new OrderPlacedEvent(
            order.getId(), order.getUserId(), order.getTotal()));
        return order;
    }
}

// Observers — decoupled listeners
@Component
public class EmailNotificationListener {
    @EventListener
    public void onOrderPlaced(OrderPlacedEvent event) {
        emailService.sendConfirmation(event.userId(), event.orderId());
    }
}

@Component
public class AnalyticsListener {
    @Async  // runs in a separate thread
    @EventListener
    public void onOrderPlaced(OrderPlacedEvent event) {
        analyticsService.trackOrder(event.orderId(), event.amount());
    }
}

Key Points to Remember

  • 1Observer defines one-to-many dependency — Subject notifies all registered observers on state change.
  • 2The "lapsed listener" problem: failing to deregister observers causes memory leaks.
  • 3Spring's @EventListener is the production Observer implementation — use it over raw Observer.
  • 4java.util.Observable is deprecated since Java 9 — avoid it.
  • 5@Async on @EventListener makes the observer run on a separate thread (fire-and-forget).
  • 6Reactive streams (RxJava, Project Reactor) extend Observer with backpressure, error handling, and operators.

Interview Questions

Sign in to ask Aria
1

What is the lapsed listener problem in Observer pattern and how do you fix it?

MediumAmazon
2

What is the difference between Observer and Pub-Sub patterns?

MediumGoogle
3

How does Spring's ApplicationEvent use the Observer pattern?

EasyAtlassian
4

How would you make Observer thread-safe?

HardNetflix
5

What is the difference between push and pull models in Observer?

MediumMicrosoft

Ask Aria about Observer Pattern

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…