Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

Observer
LLD

Observer

Learn how objects can automatically notify dependent objects when their state changes.

Observer: Pub-Sub Mechanism

The Problem It Solves

Order placement must trigger email, inventory update, analytics, and loyalty points. The naive version calls all four from placeOrder() — coupling the core flow to every consumer forever, and editing it for each new consumer. Observer inverts the dependency: the subject (order service) knows only “I have subscribers”; observers subscribe themselves and receive events.

 DIRECT COUPLING                     OBSERVER

 placeOrder() ──► emailService       Subject ──┬─► observer A
             ├─► inventorySvc        (knows    ├─► observer B
             └─► analytics           only the  └─► observer C
 adding D = edit placeOrder()        interface)  adding D = subscribe, done
                                     zero edits to subject

Mechanics

interface OrderObserver {                       // observer side
    void onOrderPlaced(Order order);
}

class OrderService implements Subject {
    private final List<OrderObserver> observers = new CopyOnWriteArrayList<>();

    public void subscribe(OrderObserver o)   { observers.add(o); }
    public void unsubscribe(OrderObserver o) { observers.remove(o); }

    public Order placeOrder(Cart cart) {
        Order order = persist(cart);            // core responsibility
        notifyObservers(order);
        return order;
    }
    private void notifyObservers(Order o) {
        for (var obs : observers) obs.onOrderPlaced(o);
    }
}

CopyOnWriteArrayList matters: an observer unsubscribing during notification (classic) would otherwise throw ConcurrentModificationException.

Push vs Pull

ModelSignatureTrade-off
PushonEvent(Order fullData)Simple; observers get possibly-unneeded data; subject couples to payload shape
PullonEvent(Subject s) — observers call back s.getOrder()Observers fetch what they need; extra chatter; subject API surface grows

Push dominates for domain events with stable payloads; pull when consumers vary wildly in needs.

Failure Semantics — the Part That Bites in Production

  • One throwing observer breaks the rest of the fan-out. Wrap dispatch per-observer:
try { obs.onOrderPlaced(o); }
catch (RuntimeException e) { log.error("observer failed", e); }  // isolate failures
  • Sync notification = observer latency adds to placeOrder() latency. Four slow observers quadruple response time — production systems hand events to executors or queues instead.

The Classic Hazard: Listener Leaks

Subscribe without unsubscribe → observer retained forever by subject → memory leak plus duplicate handling after re-registration. GUI frameworks and event buses document this prominently. Rules: pair every subscription with a lifecycle-driven unsubscribe; prefer weak references where frameworks support them.

Real-World Sightings

  • Swing/AWT listeners; Spring ApplicationEventPublisher; Kafka-style consumers at system scale (see next page).
  • Property change listeners in UI binding.

Interview Framing

  • Drawing the interface-first version scores; mentioning failure isolation + async dispatch + listener leaks closes the seniority gap.

My Private Notes

Notes are auto-saved locally to this device.