Back to Behavioral: Strategy, Observer, Command, State, Template
Q10Question 10 of 12

Design using the Observer pattern

Key Concepts
  • The Observer pattern lets a subject broadcast state changes to many subscribers without knowing who they are.
  • The subject maintains a list of observers and notifies them on each state change via a common callback interface.
  • Adding a new reaction is adding a new observer; the subject is never modified, satisfying Open/Closed.
  • One slow or failing observer must not block the others; notify defensively with per-observer exception handling.
  • Unsubscribe observers that are no longer needed to prevent the lapsed-listener memory leak.
  • At scale, the Observer idea becomes publish-subscribe over a message broker like Kafka.

Definition

The Observer pattern lets a subject broadcast state changes to many subscribers without knowing who they are.
  • Observer pattern – defines a one-to-many dependency where a subject maintains a list of observers and notifies them automatically on state changes.
  • Subject – the object whose state changes; holds a list of observers and offers subscribe(), unsubscribe(), and notify() methods.
  • Observer – implements a callback interface and reacts to notifications.
  • Decoupling – the subject depends only on the observer interface, never on concrete observers.
  • Event-driven – at scale this becomes publish-subscribe over a message broker.

Think of the Observer as the alert subscription at National Bank of India. When a stock Rajesh follows crosses a price, everyone who subscribed is notified at once: Rajesh by SMS, Priya by email, the dashboard by a refresh. The stock price (subject) does not know or care who is listening; it just announces the change and every subscriber reacts on its own.

Understanding the Concepts

What is the Observer pattern for event notification?

The Observer pattern defines a one-to-many relationship where a subject keeps a list of observers and notifies all of them when its state changes. The subject depends only on the observer interface, so adding a new reaction is adding a new observer, never editing the subject.

JAVA
package com.bank.observer;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class StockPricePublisher {
    private final String symbol;
    private BigDecimal price;
    private final List<Consumer<PriceAlert>> observers = new ArrayList<>();

    public StockPricePublisher(String symbol, BigDecimal initialPrice) {
        this.symbol = symbol;
        this.price = initialPrice;
    }

    public void subscribe(Consumer<PriceAlert> observer) {
        observers.add(observer);
    }

    public void unsubscribe(Consumer<PriceAlert> observer) {
        observers.remove(observer);
    }

    public void setPrice(BigDecimal newPrice) {
        this.price = newPrice;
        PriceAlert alert = new PriceAlert(symbol, newPrice);
        observers.forEach(o -> o.accept(alert));
    }
}

public record PriceAlert(String symbol, BigDecimal price) {}

How do you implement concrete observers?

Each observer implements the Consumer interface and reacts independently.

JAVA
package com.bank.observer;

import java.util.function.Consumer;

public class SmsAlertObserver implements Consumer<PriceAlert> {
    private final String phone;

    public SmsAlertObserver(String phone) { this.phone = phone; }

    @Override
    public void accept(PriceAlert alert) {
        System.out.println("SMS to " + phone + ": "
            + alert.symbol() + " is now INR " + alert.price());
    }
}

public class DashboardObserver implements Consumer<PriceAlert> {
    @Override
    public void accept(PriceAlert alert) {
        System.out.println("Dashboard updated: "
            + alert.symbol() + " = INR " + alert.price());
    }
}
JAVA
package com.bank.demo;

import com.bank.observer.*;
import java.math.BigDecimal;

public class ObserverDemo {
    public static void main(String[] args) {
        StockPricePublisher reliance = new StockPricePublisher(
            "RELIANCE", new BigDecimal("2400"));

        reliance.subscribe(new SmsAlertObserver("+91-98765-43210"));
        reliance.subscribe(new DashboardObserver());
        reliance.subscribe(alert -> System.out.println(
            "AUDIT: Price change for " + alert.symbol()));

        reliance.setPrice(new BigDecimal("2550"));
    }
}
TEXT
SMS to +91-98765-43210: RELIANCE is now INR 2550
Dashboard updated: RELIANCE = INR 2550
AUDIT: Price change for RELIANCE

How do you use Observer for order status changes?

When an order's status changes, the order fires one notification and independent observers handle different reactions.

JAVA
package com.bank.observer;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class OrderStatusPublisher {
    private final List<Consumer<OrderEvent>> observers = new ArrayList<>();

    public void subscribe(Consumer<OrderEvent> observer) {
        observers.add(observer);
    }

    public void statusChanged(String orderId, String status,
                              BigDecimal amount) {
        OrderEvent event = new OrderEvent(orderId, status, amount);
        for (Consumer<OrderEvent> o : observers) {
            try {
                o.accept(event);
            } catch (Exception e) {
                System.err.println("Observer failed: " + e.getMessage());
            }
        }
    }
}

public record OrderEvent(String orderId, String status, BigDecimal amount) {}
JAVA
orderPublisher.subscribe(e -> System.out.println(
    "Email to Priya: Order " + e.orderId() + " is now " + e.status()));
orderPublisher.subscribe(e -> System.out.println(
    "Loyalty: Award " + e.amount().multiply(new BigDecimal("0.01"))
    + " points for order " + e.orderId()));

orderPublisher.statusChanged("ORD-1001", "SHIPPED", new BigDecimal("15000"));
TEXT
Email to Priya: Order ORD-1001 is now SHIPPED
Loyalty: Award 150.00 points for order ORD-1001

How do you handle observer failures?

One slow or failing observer must not block the others. Catch exceptions per observer. For heavy work, dispatch asynchronously.

JAVA
for (Consumer<OrderEvent> o : observers) {
    try {
        o.accept(event);
    } catch (Exception e) {
        log.warning("Observer failed: " + e.getMessage());
        // Continue notifying remaining observers
    }
}

How does Observer scale to distributed systems?

At scale, the Observer idea becomes publish-subscribe over a message broker like Kafka. The subject publishes an event to a topic, and many consumer services react independently.

JAVA
// Local Observer
stockPublisher.subscribe(alert -> smsService.send(alert));

// Distributed Observer (Kafka)
kafkaTemplate.send("stock-prices", priceEvent);
// Consumer services subscribe to the topic independently

What are common mistakes?

The first mistake is not unsubscribing observers, causing the lapsed-listener memory leak.

JAVA
// Wrong: subscribe without unsubscribe plan
publisher.subscribe(observer); // who removes this?

// Correct: track subscription for cleanup
var sub = publisher.subscribe(observer);
// Later: sub.close() or unsubscribe in lifecycle callback

The second mistake is modifying the observer list during notification, causing ConcurrentModificationException.

When should you use the Observer pattern?

Use it when a state change in one object should trigger reactions in many independent objects, and the set of reactions changes over time. Do not use it when there is exactly one reaction that never changes.

JAVA
// Good: multiple independent reactions to a transfer
publisher.subscribe(auditListener);
publisher.subscribe(smsListener);
publisher.subscribe(dashboardListener);

// Over-engineering: only one reaction, never changes
publisher.subscribe(auditListener); // just call auditService directly

What should a senior engineer know?

The Observer pattern is the conceptual foundation of event-driven architecture. Getting failure isolation and unsubscription right is what makes it production-grade. At National Bank of India, Pankaj uses Observer for stock price thresholds and order status changes, and at scale it becomes publish-subscribe over Kafka where consumer services react independently to events on a topic.

Visual Overview

DIAGRAM
  Observer Pattern for Stock Prices
  ─────────────────────────────────
  StockPricePublisher ("RELIANCE")
       │
       │  setPrice(2550)
       │
       ├── SmsAlertObserver   → "SMS: RELIANCE = 2550"
       ├── DashboardObserver  → "Dashboard: RELIANCE = 2550"
       └── AuditObserver      → "AUDIT: Price change"

  Adding a new reaction:
       └── LoyaltyObserver    → new observer, subject unchanged
DIAGRAM
  Scaling to Distributed
  ──────────────────────
  Local:        Publisher ──notify──→ [Observer A, B, C]
  Distributed:  Producer ──Kafka──→ Topic ──→ [Consumer A, B, C]
  Same pattern, different transport.

Key Takeaway

The Observer pattern defines a one-to-many relationship where a subject keeps a list of observers and notifies every one of them automatically when its state changes. The whole point is decoupling: the subject broadcasts a change and does not know or care who is listening, so adding a new reaction is adding a new observer without ever touching the subject.

The structure is a subject offering subscribe, unsubscribe, and a notification step, plus observers that share a common interface. When a stock's price changes or an order's status changes, the subject fires one notification and each observer reacts independently: one sends an SMS, another an email, another refreshes a dashboard, and another awards loyalty points.

Two engineering concerns separate a production implementation from a toy. First, one slow or failing observer must never block the others, so notify defensively with per-observer exception handling. Second, unsubscribe observers that are no longer needed to prevent the lapsed-listener memory leak.

At scale this exact idea becomes publish-subscribe over a message broker like Kafka, where the subject publishes an event to a topic and many consumer services react independently. At National Bank of India, Pankaj uses Observer for stock price alerts and order status changes, and it is the conceptual foundation of the entire event-driven architecture.

Remember one sentence: Observer broadcasts state changes to many subscribers, decoupling the source from the reactions and forming the foundation of event-driven systems.

---

Share
Use ← → arrow keys to navigate