Behavioral Patternscritical

Observer Pattern

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

Memory anchor

Observer = YouTube subscriptions. The creator (subject) uploads a video, and every subscriber (observer) gets notified automatically. The creator has no idea who's watching — they just hit publish and the bell rings everywhere.

Expected depth

Subject maintains a list of Observers and calls notify() on state change. Observers register and deregister themselves. Classic Java: java.util.Observable (deprecated) and EventListener. Modern equivalent: event bus (Guava EventBus, Spring ApplicationEventPublisher), reactive streams (RxJava Observable, Project Reactor Flux). Observer enforces DIP: Subject depends on the Observer interface, not concrete observers.

Deep — senior internals

Observer has several known failure modes: (1) Memory leaks — Subject holds strong references to Observer; if the Observer is not explicitly removed, it is never GC'd. Solution: use WeakReference or ensure explicit unsubscription lifecycle. (2) Lapsed listener problem — deregistered observer still holds a reference to the Subject. (3) Notification order — observers are called in registration order by default; order dependency between observers is a hidden coupling. (4) Cascading updates — observer A's update triggers B's update which triggers A's update — infinite loop. (5) Exception isolation — if observer A throws, observers B–N are not called. Reactive streams (Project Reactor, RxJava) solve most of these: backpressure, error channels, composable operators, and declarative subscription management.

🎤Interview-ready answer

Observer decouples the Subject from the logic that reacts to its changes. In an e-commerce order system: OrderService is the Subject; EmailNotifier, InventoryUpdater, and AnalyticsTracker are Observers. When an order completes, the service fires a single event and each observer handles it independently. I always raise two issues: memory leaks from forgotten listener deregistration, and exception isolation (wrap each notification in try/catch so one bad observer doesn't block the rest). For production, I'd use an event bus or message queue to get delivery guarantees and async processing.

Common trap

Observer assumes synchronous in-process communication. If an observer does IO (sends email, writes to DB), the Subject blocks. For IO-heavy observers, use an async event bus or a message broker (Kafka, RabbitMQ). Do not conflate Observer (in-process, synchronous by default) with publish-subscribe (cross-process, async, broker-mediated).

Related concepts