Skip to main content

Observer

Observer defines a one-to-many dependency where a subject notifies all registered observers when its state changes. It decouples the source of change from its consumers and powers event listeners, reactive bindings, and live UI updates.

Type
Behavioral
When to Use
State Change Notification, Decoupled Listeners, Event Driven UI

Observer is a behavioral pattern that defines a one-to-many relationship between objects: when the subject changes state, every registered observer is notified automatically. It solves the problem of keeping multiple parts of a system consistent with a source of truth without hard-wiring those parts together. The subject knows that observers exist and conform to an interface, but it does not know their concrete types.

How It Works

The subject maintains a list of observers and exposes subscribe and unsubscribe operations. When its state changes, it calls a notify method that iterates the list and invokes an update callback on each observer. Notifications can be push, where the subject sends the changed data, or pull, where observers query the subject for what they need. Observers implement a common interface so the subject can treat them uniformly.

This inversion of dependencies means new observers can be added without touching the subject. The pattern underpins event listeners in UI toolkits, reactive streams, and the model-view bindings of many frameworks.

When to Use It

Use Observer when a change to one object requires changing others and you do not know in advance how many objects must change, or when an object should notify others without making assumptions about who they are. It fits UI updates reacting to model changes, cache invalidation, real-time dashboards, and domain objects that emit lifecycle events.

Trade-offs

Observers are notified in an unspecified order, so logic that depends on ordering is fragile. Cascading updates can be hard to debug because a single change may trigger a long chain of notifications. Memory leaks are a common defect: an observer that is never unsubscribed keeps the subject alive, the so-called lapsed listener problem. Synchronous notification also couples the subject's performance to the slowest observer, which is why many systems move to asynchronous Publish-Subscribe at scale.

Related Patterns

Publish-Subscribe generalizes Observer by inserting a broker or event channel between publishers and subscribers, removing the direct reference the subject holds and typically operating across process boundaries. Mediator centralizes communication between peers rather than broadcasting from one source. Command is often combined with Observer to represent the notifications as objects that can be queued or replayed. Modern reactive libraries such as RxJS, Reactor, and the various Flow APIs are a generalization of Observer that adds composition, backpressure, and rich operators for transforming streams of notifications. Many UI frameworks build their data binding directly on the observer idea, and the model-view-controller and model-view-viewmodel architectures rely on it to keep views in sync with models.

Example

A stock ticker subject holds the current price. A chart widget, an alert service, and a logger each subscribe as observers. When a new price arrives, the ticker calls notify, and all three update independently, each interpreting the change in its own way. Adding a mobile push notifier later requires only a new observer class and a single subscribe call, with no change to the ticker. To avoid the lapsed-listener leak, each observer unsubscribes when it is disposed, and in long-running systems a weak-reference registry or explicit lifecycle hooks ensure that detached observers are collected rather than kept alive forever by the subject.