In one sentence: Observer defines a one-to-many dependency between objects so that when one changes state, all its dependents are notified and updated automatically — and subscribers can come and go at runtime.
Real-world analogy
You fill in a subscription card once. From then on the paper lands on your doorstep every morning without you doing anything. You never ring the printworks to ask "is today's edition out yet?" — when there is news, it comes to you.
The paper's side of this is deliberately thin. It keeps one list, and to that list you are a line on a delivery round, not a person. It prints because there is news to print, whether four households are signed up or forty thousand, and it never asks what you do with the copy — read it, skim the sport, line the cat tray. The transaction ends at your door.
Cancel and the deliveries stop tomorrow. You owe no explanation, and the presses roll exactly as before. Neither side has to know much about the other for the whole arrangement to work.
Mapping the analogy to the pattern:
- The newspaper → the ConcreteSubject: it holds the state everyone cares about (today's news) and announces changes to it.
- The subscription list → the Subject's list of observers, the only thing it keeps about its audience.
- You, a subscriber → a ConcreteObserver, holding the update operation that decides what a new edition means for you.
- A new edition going out to the whole round →
Notify(): one change, fanned out to every observer, with no receiver named in the call. - Cancelling your subscription →
Detach()— subscribers come and go at runtime, and the subject's code never changes for it.
Another angle: a doorbell. Without one, you'd walk to the door every minute to check — that's polling, and almost every trip is wasted. The bell inverts it: you get told the moment something happens, and you spend the rest of the time doing your actual work.
The problem
GoF's motivating example is a spreadsheet and a bar chart showing the same data: edit the numbers in one and the other must redraw immediately, yet neither should have to know the other exists. Our version is a weather station. It reads a new temperature, and a phone app, a wall thermostat, and a logging service all need that number.
- Polling wastes work. If each display asks the station "changed yet?" every second, most of those calls answer "no."
- Hard-coding the list is worse. If the station calls
phone.update()andthermostat.update()directly, you must edit and recompile the station every time a new display appears — and the station now depends on every display class. - Tight coupling kills reuse. Fusing the data and its presentation into one class means you can never reuse just the data, and the merged object straddles two layers of the system.
The solution
Flip the direction. The station keeps a list of subscribers behind one small interface and never knows their concrete types:
- Define an observer interface with a single notification method.
- The subject (the station) offers
subscribeandunsubscribe, and stores whatever it's given. - On every change, the subject walks its list and calls that one method.
The subject depends only on the interface, so a new display type is a new class and zero edits to the station.
One design decision shapes everything else: how much does the notification carry? GoF names the two extremes. In the push model the subject sends detailed information about the change whether observers want it or not — fast, but the subject now assumes something about what observers need, which makes them less reusable. In the pull model the subject sends the barest "something changed" ping and observers call back to ask what; maximally ignorant of its observers, but they may have to work hard to figure out what moved. The example below pushes the temperature, because there is exactly one thing to know.
Note: GoF warns that a subject must be self-consistent before it notifies, since observers immediately turn around and query it. The trap is a subclass that calls the inherited operation (which notifies) and then updates its own fields — observers see a half-updated object. The fix is to make the notifying operation a Template Method whose last step is the notification.
Structure
Two small hierarchies, wired one-to-many:
- Subject — knows its observers (any number of them) and offers the interface for attaching and detaching them.
WeatherStationin the example, though there it is a single concrete class rather than a base class. - Observer — declares the updating interface that anything wanting notifications must implement. Here that's
Observer::onTemperature. - ConcreteSubject — stores the state that observers care about, and notifies them whenever that state changes.
- ConcreteObserver — holds a reference to its concrete subject, stores state that must stay consistent with the subject's, and implements the update operation to reconcile the two.
PhoneDisplayandThermostatplay this role.
C++ example
A weather station with two displays. One of them unsubscribes halfway through, and the next reading skips it.
#include <algorithm>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
class Observer {
public:
virtual ~Observer() = default;
virtual void onTemperature(double celsius) = 0;
virtual std::string name() const = 0;
};
class WeatherStation {
public:
void subscribe(Observer* o) {
observers_.push_back(o);
std::cout << o->name() << " subscribed\n";
}
void unsubscribe(Observer* o) {
observers_.erase(std::remove(observers_.begin(), observers_.end(), o),
observers_.end());
std::cout << o->name() << " unsubscribed\n";
}
void setTemperature(double celsius) {
std::cout << "-- station reads " << celsius << "C --\n";
for (Observer* o : observers_) o->onTemperature(celsius);
}
private:
std::vector<Observer*> observers_;
};
class PhoneDisplay : public Observer {
public:
void onTemperature(double c) override { std::cout << " Phone shows " << c << "C\n"; }
std::string name() const override { return "Phone"; }
};
class Thermostat : public Observer {
public:
void onTemperature(double c) override {
std::cout << " Thermostat: heating " << (c < 18.0 ? "ON" : "OFF") << "\n";
}
std::string name() const override { return "Thermostat"; }
};
int main() {
WeatherStation station;
auto phone = std::make_unique<PhoneDisplay>();
auto thermostat = std::make_unique<Thermostat>();
station.subscribe(phone.get());
station.subscribe(thermostat.get());
station.setTemperature(21.5);
station.unsubscribe(phone.get());
station.setTemperature(15.2);
}Phone subscribed
Thermostat subscribed
-- station reads 21.5C --
Phone shows 21.5C
Thermostat: heating OFF
Phone unsubscribed
-- station reads 15.2C --
Thermostat: heating ONThe station's code never changed between the two readings — the subscriber list did, and the second broadcast simply reached one fewer object.
When should you use it?
GoF gives three situations. Use Observer when:
- An abstraction has two aspects, one depending on the other. Putting them in separate objects lets you vary and reuse each on its own — the classic case being data and the views that display it.
- A change to one object requires changing others, and you don't know how many. The count is a runtime fact, not a compile-time one.
- An object should be able to notify others without assuming who they are. You explicitly do not want these objects tightly coupled.
Pros and cons
Pros (GoF's consequences)
- Subjects and observers vary independently — you can reuse a subject without its observers and vice versa, and add observers without modifying the subject or the other observers.
- Abstract, minimal coupling — all a subject knows is that it holds a list of things conforming to one small interface. Because the coupling is abstract, subject and observer can even sit in different layers of the system without breaking the layering.
- Broadcast communication — the notification doesn't name a receiver. The subject's only duty is to announce; it's up to each observer to handle or ignore.
Cons
- Unexpected updates. This is GoF's headline liability: observers know nothing about each other, so they can't see what a change really costs. One innocuous-looking write to the subject can set off a cascade of updates through observers and their dependents, and badly specified dependencies produce spurious updates that are miserable to trace.
- The bare update protocol says what happened, not what changed, so without a richer protocol observers may have to work to deduce it.
- Notification order is unspecified, so observers must not depend on each other.
- Lifetime bugs are easy — a destroyed observer that never unsubscribed leaves a dangling pointer.
Note: GoF flags the mirror-image lifetime bug too: deleting a subject must not leave dangling references in its observers. Simply deleting the observers along with it is not an option, since other objects may reference them or they may be watching other subjects; instead the subject should notify observers as it dies so they can drop their reference. In the example the station holds raw
Observer*because it does not own the displays — the right ownership choice, but it means an observer must unsubscribe before it dies. For subscribers with unpredictable lifetimes, storestd::weak_ptr<Observer>and drop the expired ones during notification.
How to remember it
Memory hook: "A newspaper subscription." The publisher never phones round asking who wants today's issue — it prints once and delivers to whoever is on the list, and cancelling stops the deliveries without the publisher knowing or caring why. Contrast with Mediator, the pattern it is most often confused with: Observer distributes communication (many subjects each broadcasting to their own observers), while a Mediator centralizes it (one object that knows everyone and routes between them).
Related patterns
- Mediator — when the dependencies get complicated, GoF suggests a ChangeManager: one object that owns the subject-to-observer mapping, defines the update strategy, and does the updating on a subject's behalf. That ChangeManager is a Mediator between subjects and observers, and it can avoid notifying an observer twice when it depends on several subjects at once.
- Singleton — there is normally only one ChangeManager and everyone needs to reach it, so GoF notes it is a natural Singleton.
- Template Method — GoF's recommended way to guarantee the subject is self-consistent when it notifies: make
Notify()the final step of a template method that subclasses fill in.