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
| Model | Signature | Trade-off |
|---|---|---|
| Push | onEvent(Order fullData) | Simple; observers get possibly-unneeded data; subject couples to payload shape |
| Pull | onEvent(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.
Premium Content
Unlock Observer and all premium lessons with a subscription.
From ₹199.99/year — See plans