Observer
Intent
- Define a one-to-many dependency between objects so that when one object
changes state, all its dependents are notified and updated
automatically. [GoF, p293]
- Encapsulate the core (or common or engine) components in a Subject
abstraction, and the variable (or optional or user interface) components
in an Observer hierarchy.
- The "View" part of Model-View-Controller.
Problem
A large monolithic design does not scale well as new graphing or
monitoring requirements are levied.
Structure Summary
Structure category: wrapper + inheritance hierarchy
Similar patterns:
Builder
Bridge
State
Discussion
Define an object that is the "keeper" of the data model and/or business
logic (the Subject). Delegate all "view" functionality to decoupled and
distinct Observer objects. Observers register themselves with the
Subject as they are created. Whenever the Subject changes, it
broadcasts to all registered Observers that it has changed, and each
Observer queries the Subject for that subset of the Subject's state
that it is responsible for monitoring.
This allows the number and "type" of "view" objects to be configured
dynamically, instead of being statically specified at compile-time.
The protocol described above specifies a "pull" interaction model. Instead
of the Subject "pushing" what has changed to all Observers, each Observer is
responsible for "pulling" its particular "window of interest" from the
Subject. The "push" model compromises reuse, while the "pull" model is
less efficient.
Issues that are discussed, but left to the discretion of the designer,
include: implementing event compression (only sending a single change
broadcast after a series of consecutive changes has occurred), having a
single Observer monitoring multiple Subjects, and ensuring that a
Subject notify its Observers when it is about to go away.
The Observer pattern captures the lion's share of the Model-View-Controller
architecture that has been a part of the Smalltalk community for years.
Structure
Subject represents the core (or independent or common or engine)
abstraction. Observer represents the variable (or dependent or
optional or user interface) abstraction. The Subject prompts the
Observer objects to do their thing. Each Observer can call back
to the Subject as needed.
Example
The Observer defines a one-to-many relationship so that when one object
changes state, the others are notified and updated automatically. Some
auctions demonstrate this pattern. Each bidder possesses a numbered
paddle that is used to indicate a bid. The auctioneer starts the
bidding, and "observes" when a paddle is raised to accept the bid. The
acceptance of the bid changes the bid price which is broadcast to all
of the bidders in the form of a new bid. [Michael Duell, "Non-software
examples of software design patterns", Object Magazine,
Jul 97, p54]
Check list
- Differentiate between the core (or independent) functionality and
the optional (or dependent) functionality.
- Model the independent functionality with a "subject" abstraction.
- Model the dependent functionality with an "observer" hierarchy.
- The Subject is coupled only to the Observer base class.
- The client configures the number and type of Observers.
- Observers register themselves with the Subject.
- The Subject broadcasts events to all registered Observers.
- The Subject may "push" information at the Observers, or, the
Observers may "pull" the information they need from the Subject.
Before and after
Before | | After |
// The number and type of "user interface" (or
// dependent) objects is hard-wired in the
// Subject class. The user has no ability to
// affect this configuration.
class DivObserver {
int m_div;
public:
DivObserver( int div ) { m_div = div; }
void update( int val ) {
cout << val << " div " << m_div << " is "
<< val / m_div << '\n';
} };
class ModObserver {
int m_mod;
public:
ModObserver( int mod ) { m_mod = mod; }
void update( int val ) {
cout << val << " mod " << m_mod << " is "
<< val % m_mod << '\n';
} };
class Subject {
int m_value;
DivObserver m_div_obj;
ModObserver m_mod_obj;
public:
Subject() : m_div_obj(4), m_mod_obj(3) { }
void set_value( int value ) {
m_value = value;
notify();
}
void notify() {
m_div_obj.update( m_value );
m_mod_obj.update( m_value );
} };
int main( void ) {
Subject subj;
subj.set_value( 14 );
}
// 14 div 4 is 3
// 14 mod 3 is 2
| |
// The Subject class is now decoupled from
// the number and type of Observer objects.
// The client has asked for two DivObserver
// delegates (each configured differently),
// and one ModObserver delegate.
class Observer {
public:
virtual void update( int value ) = 0;
};
class Subject {
int m_value;
vector m_views;
public:
void attach( Observer* obs ) {
m_views.push_back( obs );
}
void set_val( int value ) {
m_value = value; notify();
}
void notify() {
for (int i=0; i < m_views.size(); ++i)
m_views[i]->update( m_value );
} };
class DivObserver : public Observer {
int m_div;
public:
DivObserver( Subject* model, int div ) {
model->attach( this );
m_div = div;
}
/* virtual */ void update( int v ) {
cout << v << " div " << m_div << " is "
<< v / m_div << '\n';
} };
class ModObserver : public Observer {
int m_mod;
public:
ModObserver( Subject* model, int mod ) {
model->attach( this );
m_mod = mod;
}
/* virtual */ void update( int v ) {
cout << v << " mod " << m_mod << " is "
<< v % m_mod << '\n';
} };
int main( void ) {
Subject subj;
DivObserver divObs1( &subj, 4 );
DivObserver divObs2( &subj, 3 );
ModObserver modObs3( &subj, 3 );
subj.set_val( 14 );
}
// 14 div 4 is 3
// 14 div 3 is 4
// 14 mod 3 is 2
|
Rules of thumb
Chain of Responsibility, Command, Mediator, and Observer, address how
you can decouple senders and receivers, but with different trade-offs.
Chain of Responsibility passes a sender request along a chain of
potential receivers. Command normally specifies a sender-receiver
connection with a subclass. Mediator has senders and receivers
reference each other indirectly. Observer defines a very decoupled
interface that allows for multiple receivers to be configured at
run-time. [GoF, p347]
Mediator and Observer are competing patterns. The difference between
them is that Observer distributes communication by introducing
"observer" and "subject" objects, whereas a Mediator object
encapsulates the communication between other objects. We've found it
easier to make reusable Observers and Subjects than to make reusable
Mediators. [GoF, p346]
On the other hand, Mediator can leverage Observer for dynamically
registering colleagues and communicating with them. [GoF, p282]