Observer pattern

On this page 7

The observer pattern is a behavioral design pattern in which an object called the subject keeps a list of dependent objects called observers and notifies them automatically whenever its state changes. In software architecture and programming it establishes a one-to-many dependency without tight coupling: the subject knows only that its observers implement a common notification interface, not what they do with the update.

The pattern was cataloged by the “Gang of Four” in 1994, but its idea is older and now appears everywhere: GUI event listeners, data binding in MVC and MVVM frameworks, reactive streams, spreadsheet recalculation and domain events. Understanding it explains both the flexibility of event-driven code and its typical problems, such as memory leaks, unpredictable ordering and update cascades.

How the observer pattern works

The classic structure has four roles. The subject interface offers methods to attach and detach observers. A concrete subject holds the state of interest and calls notify() when it changes. The observer interface declares an update() method, and each concrete observer implements it to react, for example by redrawing a chart or writing a log entry.

ParticipantResponsibilityExample
SubjectDefines attach, detach and notify operationsAn Observable base class
Concrete subjectStores state and triggers notificationsA stock price feed
ObserverDeclares the update interfaceA PriceListener interface
Concrete observerReacts to the changeA chart widget or an alert service
Participants in the observer pattern

The sequence is simple: observers register with the subject, the subject’s state changes, the subject iterates over its list and calls each observer, and observers may later unregister. Because the subject depends only on the interface, new observers can be added without modifying the subject, which follows the open-closed principle from the SOLID principles.

Push and pull notification models

Implementations differ in how much data travels with the notification. In the push model, the subject sends the changed data with the update call. In the pull model, the subject only signals that something changed, and each observer queries the subject for the details it needs.

AspectPush modelPull model
Data in the notificationChanged values or an event objectOnly a reference to the subject
CouplingObservers depend on the event formatObservers depend on the subject’s query methods
EfficiencyGood when all observers need the same dataGood when observers need different data
Consistency riskLow, the snapshot is sent togetherState may change again before the pull
Typical useEvent objects in UI frameworksModel-view refresh in simple MVC
Comparing push and pull models

Modern code often uses a hybrid: a small event object describes what changed, and observers can fetch more detail if needed. Event objects also make it easy to filter notifications by event type.

Several neighboring concepts share the observer idea but change who knows whom and when delivery happens. The distinction matters when choosing an architecture.

MechanismCouplingDeliveryTypical scope
Observer patternObservers register directly with the subjectUsually synchronous, in processObjects inside one application
Publish-subscribePublishers and subscribers know only a broker or topicOften asynchronousModules or distributed services
Event busComponents share a central dispatcherSynchronous or queuedLarge applications with many modules
Reactive streamsObservers subscribe to composable streamsAsynchronous with backpressureData pipelines and UI state
CallbackOne caller passes one functionSingle notification targetSimple asynchronous APIs
Observer and related notification mechanisms

In MVC and MVVM, the observer pattern is the mechanism that keeps views synchronized with models: the view observes the model, so changes in data appear on screen without the model knowing anything about rendering. This supports separation of concerns.

Implementing the observer pattern well

Most languages and frameworks provide built-in support, such as events and delegates in C#, listener interfaces in Java, EventTarget in browser JavaScript and signals in many UI toolkits. Writing a custom subject is still common in domain code, and a few decisions determine whether it remains reliable.

  • Copy the observer list before notifying, so observers can detach themselves during an update without breaking the iteration.
  • Define ordering explicitly if it matters; otherwise document that observers must not depend on order.
  • Isolate failures: one throwing observer should not prevent others from receiving the notification.
  • Return a subscription handle from attach so callers can reliably unsubscribe later.
  • Decide on threading: notify on the caller’s thread, a UI thread or a queue, and document the choice.

For high-frequency changes, batching or debouncing notifications avoids redundant work. A form that updates on every keystroke can coalesce changes so that expensive observers, such as validation or network calls, run only after input pauses.

Pitfalls of the observer pattern

The flexibility of implicit notifications comes at a cost: control flow becomes harder to follow, because a single state change can trigger code in many places. The most common problems and their remedies are listed below.

ProblemCauseRemedy
Lapsed listener memory leakObserver never unsubscribes, subject keeps it aliveUnsubscribe on disposal or use weak references
Update cascadesObservers change other subjects in turnBatch updates, avoid cyclic dependencies
Infinite loopsTwo objects observe and update each otherCheck for real changes before notifying
Order dependenceObservers assume a fixed call orderRemove the assumption or use explicit stages
Thread-safety bugsNotifications arrive on unexpected threadsMarshal to a defined thread or lock the list
Typical problems and remedies

The lapsed listener problem is especially frequent in long-running GUI applications: a closed window remains in memory because a global service still holds a reference to it as an observer. Tying subscriptions to the lifecycle of the observer solves most of these leaks.

A non-technical view

The observer pattern works like a newsletter subscription. You sign up once. Whenever there is news, it arrives automatically. You never have to keep checking the website.

In a program, one part holds some information, and other parts subscribe to it. When the information changes, every subscriber is told at once. The sender does not need to know what each subscriber does with the news, which keeps the program flexible and easy to extend.

A concrete example

A logistics company builds a dashboard that tracks delivery vehicles. A vehicle position service acts as the subject and receives GPS updates every few seconds. Three observers subscribe to it: a map widget that moves the vehicle icon, an arrival estimator that recalculates delivery times and an alert module that warns dispatchers when a vehicle leaves its planned route.

When the team later adds a fourth observer that records fuel efficiency, they do not touch the position service at all; they simply register the new component. During testing they discover that closed map tabs were never unsubscribing, so memory grew all day. Binding each subscription to the tab’s lifecycle fixed the leak, and the dashboard now runs for weeks with stable memory use.