Rant: some of willchan’s thoughts on WeakPtr, for those who care to read criticizes the observer pattern for murking dependency chains. Investigate more in this regard.
Intent
A one-to-many dependency between object so that when one object (subject) changes, all its dependents (observers) are notified and update automatically.
A meta-point is that while we could achieve consistency by making the classes tightly coupled, we don’t want to do that because tight coupling reduces reusability.
What are the tradeoffs between the observer design pattern, and having the observer pass callbacks to the subject?
posits that callback patterns are more tightly coupled than a subject/observer pattern. Furthermore, callbacks are typically meant to be short-lived.
Need to think about this some more. Not totally sold.
Sample Code
An abstract class defines the Observer
interface:
class Subject;
class Observer {
public:
virtual ~Observer();
// To support multiple subjects for each observer, we pass
// `changed_subject`.
virtual void Update(Subject* changed_subject);
protected:
Observer();
};
An abstract class defines the Subject
interface:
class Subject {
public:
virtual ~Subject();
virtual void Attach(Observer*);
virtual void Detach(Observer*);
virtual void Notify();
protected:
Subject();
private:
// Notice that we do not make assumptions on the number of observers.
//
// We also do not make assumptions on the concrete classes. As long as
// the class implements the Observer interface, we're good! The
// coupling is abstract and minimal.
List<Observer*> observers_;
};
void Subject::Attach(Observer* observer) {
observers_.Append(observer);
}
void Subject::Detach(Observer* observer) {
observers_.Remove(observer);
}
void Subject::Notify() {
// In this design (pull model), we do not tell the observers what
// exactly changed in `this`. It is up to the concrete observer to
// query the concrete subject. The concrete observer can also choose
// to ignore the notification.
for (Observer* observer : observers_) observer->Update(this);
}
Another design gotcha is observers_
is observers_
getting modified while it
is being looped over in Subject::Notify
. For some operations, the iterator
may get invalidated.
In Chromium, is designed to be resistant to
same-thread modifications of the underlying std::vector
. approaches the problem in a multi-threaded
context, with the tradeoff being that notifications get silently dropped if an
observer is removed on one thread, while another thread is notifying observers.
At the very least, be aware that the problem exists.
An example concrete Subject
, ClockTimer
, that stores and maintains the time
of day:
class ClockTimer : public Subject {
public:
ClockTimer();
virtual int GetHour();
virtual int GetMinute();
virtual int GetSecond();
// Methods that eventually call `Notify()` should be labelled as such,
// so that callers can be aware of the cost of a seemingly innocuous
// operation.
void Tick();
};
// Called by an internal timer at regular intervals.
void ClockTimer::Tick() {
// Update internal-time keeping state.
// ...
Notify();
}
An example concrete Observer
, DigitalClock
, that displays the time of day.
class DigitalClock: public Widget, public Observer {
public:
DigitalClock(ClockTimer*);
virtual ~DigitalClock();
// From Observer
void Update(Subject*) override;
// Widget
void Draw() override;
private:
ClockTimer* subject_;
};
DigitalClock::DigitalClock(ClockTimer* subject) {
subject_ = subject;
subject_->Attach(this);
}
DigitalClock::~DigitalClock() {
subject_->Detach(this);
}
void DigitalClock::Update(Subject* changed_subject) {
if (changed_subject == subject_) Draw();
}
void DigitalClock::Draw() {
// Get the new values from the subject.
int hour = subject_->GetHour();
int minute = subject_->GetMinute();
int second = subject->GetSecond();
// Draw the digital clock.
}
A possible source of bugs is an observer that gets destroyed without removing itself from its subjects' observer lists. A subject will then try to notify an observer that has already been destroyed, leading to a use-after-free (UAF) bug. In Chromium, a couple of safeguards exist to avoid this UAF.
Observers subclass from
CheckedObserver
, and subjects store the observers in anObserverList
. Iterating over a destroyedObserver
in anObserverList
leads to a crash, which avoids the more severe UAF.Concrete observers hold a member variable,
ScopedObservation
orScopedMultiSourceObservation
, that removes registered observation(s), if any, when the observer is destroyed. That way, observers need not explicitly remove themselves from the subject(s)’s observer list.
Usage:
ClockTimer* timer = new ClockTimer();
DigitalClock digital_clock = new DigitalClock(timer);

Commentary on Design Decisions
Communication Flow
If concrete_observer
calls concrete_subject->SetState(new_state)
, then
concrete_observer
should hold off making any internal updates until it
receives an update notification from concrete_subject
. This deferral is
important because there may be other objects that call
concrete_subject->SetState(new_state)
.
We also don’t want to do two internal updates in concrete_observer
as the
first one is jumping the gun. Also, what if concrete_subject
rejects the
new_state
? Overly eager updates could make concrete_observer
inconsistent.
Who calls Subject::Notify
? If state-setting ops on Subject
call Notify
,
we might have several consecutive ops cause several consecutive updates, which
may be inefficient. If clients are responsible, then errors are likely since
some clients might forget to do it.
Avoiding Dangling References
Deleting a subject should not produce dangling references in its observers. One solution is have the subject notify its observers when as it is deleted, so that observers can reset their reference to it. Deleting the observers is not a solution because the observers might be referenced elsewhere, e.g. listening to other subjects.
Maintaining Consistency of the Subject
We also need to ensure that the subject is self-consistent before notification. For example, this code is buggy:
void MySubject::Operation(int new_value) {
BaseClassSubject::Operation(new_value);
// Trigger notification.
my_internal_value_ += new_value;
// Update subclass state (too late!)
}
One solution is to send notifications from template methods in the abstract
Subject
class, e.g.
void Text::Cut(TextRange r) {
ReplaceRange(r); // Redefined in subclasses.
Notify();
}
Specificity/Efficiency of Update
Protocols
There’s a tradeoff on the specificity of Update
protocols. In the push model,
the subject sends detailed information about the change, while in the pull
model, the subject sends out the most minimal notification, and the observers
must explicitly ask for details. The push model makes observers less re-usable,
while the pull method may be inefficient.
Allowing observers to register for specific events of interest can improve update efficiency. An API like this one is a possible solution:
void Subject::Attach(Observer*, Aspect& interest);
void Observer::Update(Subject*, Aspect& interest);
A further step could be the Subject
only sending notifications to observers
that are interested in the Aspect
that has been affected by the subject’s
state change.
Alternatively, a common pattern that I see in Chromium is subclassing the base
Observer
interface, and providing more specific APIs, e.g.
ClockTimerObserver::OnHourChanged(ClockTimer* timer, int new_hour)
.
In some cases, the updates may inundate the observer, e.g. the subject’s changing state several times a second. The observer may incorporate a timer to represent the approximate state of the model at a regular interval.
Orchestrating Complex Relationships Between Subjects and Observers
The dependency relationships between subjects and observers may be complex, e.g.
an operation that involves changes to several interdependent subjects, and there
is a need to ensure that observers are notified only after all subjects have
been modified, that a ChangeManager
is useful:
void Subject::Notify() {
change_manager_->Notify();
}
class ChangeManager {
public:
Register(Subject*, Observer*);
Unregister(Subject* Observer*);
Notify();
}
void ChangeManager::Notify() {
// A naive implementation that sends notifications to all observers.
for (Subject* s : subjects_) {
for (Observer* o : s->observers) {
o->Update(s);
}
}
}
ChangeManager::Notify
can be smarter, e.g. maintaining a DAG such that if an
Observer* o
observes multiple subjects, then o->Update(s)
will only be
called once to avoid redundant updates.
The ChangeManager
is an instance of the Mediator
pattern.
Combining Subject
and Observer
Interfaces in the Same Class
Note that some languages, e.g. Smalltalk, lack multiple inheritance (MI). In
such cases, it makes sense to combine the Observer
and Subject
interface in
the same class.
The lack of MI is not an anomaly. C++ got MI because it is a hybrid (contains primitives and objects) language that couldn’t enforce a single monolithic class hierarchy, unlike Smalltalk.
MI’s most famous drawback is the diamond problem being the most prominent con.
For example: D
inherits from both C
and B
, who both inherit from A
. Both
B
and C
override A::Foo
, but D
does not; if d->Foo()
is called, which
Foo
gets called?
A good Occam’s Razor is, “If you don’t need to upcast to all of the base classes, prefer composition to multiple inheritance.”
References
- Design Patterns > Behavioral Patterns > 7. Observer. Erich Gamma; Richard Helm; Ralph Johnson; John Vlissides. Oct 21, 1994.
notes that , the primary source, glosses over nitty-gritties. e.g. the “message queue” server. Maybe reading through these will provide a larger perspective to design decisions made in Chromium regarding observers.