Last Updated: February 23, 2026
The Observer Design Pattern is a behavioral pattern that defines a one-to-many dependency between objects so that when one object (the subject) changes its state, all its dependents (observers) are automatically notified and updated.
This pattern shines in scenarios where:
Let’s walk through a real-world example to see how we can apply the Observer Pattern to build a flexible, extensible, and loosely coupled notification system.
Imagine you are building a Fitness Tracker App that connects to a wearable device. The device continuously streams real-time fitness data: steps taken, active minutes, and calories burned. This data flows into a central FitnessData object.

Now, multiple modules within your app need to react to these updates:
| Module | Responsibility |
|---|---|
| LiveActivityDisplay | Shows real-time stats on the dashboard |
| ProgressLogger | Persists data to a database for trend analysis |
| GoalNotifier | Sends alerts when the user hits milestones |
In a straightforward implementation, FitnessData directly holds and manages references to all dependent modules. It knows about each one, creates them, and calls their specific methods whenever data changes.
Here is how the client uses the naive approach:
This works initially, but let us think about what happens as the application grows.
FitnessData holds direct references to LiveActivityDisplay, ProgressLogger, and NotificationService. It knows their concrete types, their method signatures, and their construction logic.
If any of these classes change their interface, or if you want to replace one with a different implementation, you must modify FitnessData.
What happens when you want to add a WeeklySummaryGenerator? Or a SocialSharingService that posts achievements to social media?
Each new feature requires you to:
FitnessDatanewFitnessDataPushed() methodThe class is open for modification when it should be closed.
Modules like the NotificationService or ProgressLogger can’t be added or removed at runtime. What if the user disables notifications in their settings?
You will need to add conditionals to manually enable/disable parts of the code, making things fragile and error-prone.
FitnessData should have one job: managing fitness metrics. Instead, it is now responsible for UI updates, database logging, and notification logic. This violates the Single Responsibility Principle and makes the class difficult to test in isolation.
As the number of dependents grows, newFitnessDataPushed() becomes a lengthy sequence of method calls, each with different parameters and error handling requirements. Every developer who adds a feature must understand and modify this method.
We need a better, scalable way to solve this problem, something that allows:
FitnessData to broadcast changes to multiple listeners, without knowing who they areThis is exactly what the Observer pattern provides.
The Observer Design Pattern provides a clean and flexible solution to the problem of broadcasting changes from one central object (the Subject) to many dependent objects (the Observers) all while keeping them loosely coupled.
Two characteristics define the pattern:
Think about a newspaper subscription. You subscribe to a newspaper publisher. Every morning, the publisher prints the paper and delivers a copy to every subscriber on its list. The publisher does not know whether you read the sports section, clip coupons, or just check the headlines. It does not care. It delivers the paper, and each subscriber decides what to do with it.
When you cancel your subscription, the deliveries stop. When a new neighbor subscribes, they start getting the paper. The publisher's printing logic never changes.
The Observer pattern works the same way: the subject (publisher) broadcasts updates, and observers (subscribers) react however they choose.
Declares the interface for managing observers, registering, removing, and notifying them. Defines registerObserver(), removeObserver(), and notifyObservers() methods.
The subject holds a list of observers typed to the Observer interface, not to concrete classes. This means any class that implements the Observer interface can register, and the subject never needs to know what it is.
Declares the update() method that the subject calls when its state changes. All modules that want to listen to fitness data changes will implement this interface.
FitnessData)Implements the Subject interface. Holds the actual state and notifies observers when that state changes.
Maintain a list of registered observers and calls notifyObservers() whenever its state changes.
LiveActivityDisplay)Implements the Observer interface. Defines what happens when the subject's state changes. When update() is called, each observer pulls relevant data from the subject and performs its own logic (e.g., update UI, log progress, send alerts).
The Observer workflow follows these steps:
Step 1: The client creates a concrete subject and one or more concrete observers.
Step 2: Each observer registers itself with the subject by calling registerObserver().
Step 3: The subject adds the observer to its internal list.
Step 4: When the subject's state changes, it calls notifyObservers(), which iterates through the list and calls update() on each observer.
Step 5: Each observer receives the notification and pulls the data it needs from the subject via getter methods.
Step 6: To stop receiving updates, an observer calls removeObserver(). The subject removes it from the list. Future notifications skip this observer.
Let us refactor the fitness tracker using the Observer pattern, step by step.
Each observer receives a reference to the subject and can pull whatever data it needs. This keeps the interface stable even as FitnessData gains new fields.
The subject interface provides methods for managing observers: registering, removing, and notifying them.
FitnessData implements the subject interface. It manages the observer list and calls notifyObservers() automatically whenever new data arrives.
Notice that FitnessData no longer imports, creates, or references any concrete observer. It just maintains a list of FitnessDataObserver references and iterates through them. The class has gone from knowing about three specific modules to knowing about zero.
Each observer implements FitnessDataObserver and defines its own update() logic.
Now we wire everything together. The client creates the subject and observers, registers the observers, and simulates fitness updates.
Compare this with the naive approach. FitnessData no longer creates or holds references to any specific module. Observers register themselves, receive updates through a common interface, and can be added or removed at any time. The daily reset notification reaches only the currently registered observers, so removing the logger means it stops getting updates immediately.
FitnessData does not know who is listening. It just broadcasts to a list of interfaces.WeeklySummaryGenerator) only requires implementing FitnessDataObserver and calling registerObserver(). Zero changes to FitnessData.FitnessData manages fitness metrics and nothing else.FitnessData and calling update() directly.The real test of any design pattern is what happens when requirements change. The product team wants a new feature: a weekly summary generator that accumulates fitness data throughout the week and produces a summary report every Sunday.
With the Observer pattern, we do not touch FitnessData at all. We just create a new observer and register it.
To use it, just register it:
The key point: we added a completely new feature to the fitness tracker without modifying a single line in FitnessData, LiveActivityDisplay, ProgressLogger, or GoalNotifier. This is the Open/Closed Principle in action.
Let us work through a second example to reinforce the pattern. This time, we are building a stock price notification system. A StockExchange subject publishes price updates for stocks.
Multiple observers react to these updates: a PriceDisplay shows current prices, an AlertService notifies traders when prices cross thresholds, and a TradingBot executes trades based on price movements.
The same pattern, different domain. StockExchange has no idea whether it is feeding a display, an alert system, or a trading bot. It just calls onPriceUpdate() on whatever observers are registered.
Adding a new consumer, say a PortfolioTracker that recalculates portfolio value on every price change, means creating one new class that implements StockObserver. Nothing else changes.