Callbacks
Functor
Command
Strategy
Observer
Like
the other forms of callback: a hook point where you can change code. Difference
is in observer’s completely dynamic nature. Often used for the specific
case of changes based on other object’s change of state, but is also the
basis of event management. Anytime you want to decouple the source of the call
from the called code in a completely dynamic way.
The
observer
pattern solves a fairly common problem: What if a group of objects needs to
update themselves when some other object changes state? This can be seen in the
“model-view” aspect of Smalltalk’s MVC
(model-view-controller), or the almost-equivalent “Document-View
Architecture.” Suppose that you have some data (the
“document”) and more than one view, say a plot and a textual view.
When you change the data, the two views must know to update themselves, and
that’s what the observer facilitates.
There
are two types of objects used to implement the observer pattern in the
following code. The Observable
class keeps track of everybody who wants to be informed when a change happens,
whether the “state” has changed or not. When someone says
“OK, everybody should check and potentially update themselves,” the
Observable
class performs this task by calling the notifyObservers( )
member function for each observer on the list. The
notifyObservers( )
member function is part of the base class
Observable. There
are actually two “things that change” in the observer pattern: the
quantity of observing objects and the way an update occurs. That is, the
observer pattern allows you to modify both of these without affecting the
surrounding code.
There
are a number of ways to implement the observer pattern, but the code shown here
will create a framework from which you can build your own observer code,
following the example. First, this interface describes what an observer looks
like:
//: C25:Observer.h
// The Observer interface
#ifndef OBSERVER_H
#define OBSERVER_H
class Observable;
class Argument {};
class Observer {
public:
// Called by the observed object, whenever
// the observed object is changed:
virtual void
update(Observable* o, Argument * arg) = 0;
};
#endif // OBSERVER_H ///:~
Since
Observer
interacts with
Observable
in this approach,
Observable
must be declared first. In addition, the
Argument
class is empty and only acts as a base class for any type of argument you wish
to pass during an update. If you want, you can simply pass the extra argument
as a
void*;
you’ll have to downcast in either case but some folks find
void*
objectionable.
Observer
is an “interface” class that only has one member function,
update( ).
This function is called by the object that’s being observed, when that
object decides its time to update all it’s observers. The arguments are
optional; you could have an
update( )
with no arguments and that would still fit the observer pattern; however this
is more general – it allows the observed object to pass the object that
caused the update (since an
Observer
may
be registered with more than one observed object) and any extra information if
that’s helpful, rather than forcing the
Observer
object to hunt around to see who is updating and to fetch any other information
it needs.
The
“observed object” that decides when and how to do the updating will
be called the
Observable:
//: C25:Observable.h
// The Observable class
#ifndef OBSERVABLE_H
#define OBSERVABLE_H
#include <set>
#include "Observer.h"
class Observable {
bool changed;
std::set<Observer*> observers;
protected:
virtual void setChanged() { changed = true; }
virtual void clearChanged(){ changed = false; }
public:
virtual void addObserver(Observer& o) {
observers.insert(&o);
}
virtual void deleteObserver(Observer& o) {
observers.erase(&o);
}
virtual void deleteObservers() {
observers.clear();
}
virtual int countObservers() {
return observers.size();
}
virtual bool hasChanged() { return changed; }
// If this object has changed, notify all
// of its observers:
virtual void notifyObservers(Argument* arg=0) {
if(!hasChanged()) return;
std::set<Observer*>::iterator it;
for(it = observers.begin();
it != observers.end(); it++)
(*it)->update(this, arg);
clearChanged(); // Not "changed" anymore
}
};
#endif // OBSERVABLE_H ///:~
Again,
the design here is more elaborate than is necessary; as long as there’s a
way to register an
Observer
with an
Observable
and for the
Observable
to update its
Observers,
the set of member functions doesn’t matter. However, this design is
intended to be reusable (it was lifted from the design used in the Java
standard library). As mentioned elsewhere in the book, there is no support for
multithreading in the Standard C++ libraries, so this design would need to be
modified in a multithreaded environment.
Observable
has a flag to indicate whether it’s been changed. In a simpler design,
there would be no flag; if something happened, everyone would be notified. The
flag allows you to wait, and only notify the
Observers
when you decide the time is right. Notice, however, that the control of the
flag’s state is
protected,
so that only an inheritor can decide what constitutes a change, and not the end
user of the resulting derived
Observer
class.
The
collection of
Observer
objects is kept in a
set<Observer*>
to prevent duplicates; the
set
insert( )
,
erase( ),
clear( )
and
size( )
functions are exposed to allow
Observers
to be added and removed at any time, thus providing run-time flexibility.
Most
of the work is done in
notifyObservers( ).
If the
changed
flag has not been set, this does nothing. Otherwise, it moves through the
set
and calls back to the
update( )
member function of each
Observer.
Finally, it clears the
changed
flag so repeated calls to
notifyObservers( )
won’t waste time.
At
first it may appear that you can use an ordinary
Observable
object to manage the updates. But this doesn’t work; to get an effect, you
must
inherit from
Observable
and somewhere in your derived-class code call setChanged( ).
This is the member function that sets the “changed” flag, which
means that when you call notifyObservers( )
all of the observers will, in fact, get notified.
Where
you call
setChanged( )
depends on the logic of your program.
Now
we encounter a dilemma. An object that should notify its observers about things
that happen to it – events or changes in state – might have more
than one such item of interest. For example, if you’re dealing with a
graphical user interface (GUI) item – a button, say – the items of
interest might be the mouse clicked the button, the mouse moved over the
button, and (for some reason) the button changed its color. So we’d like
to be able to report all of these events to different observers, each of which
is interested in a different type of event.
The
problem is that we would normally reach for multiple inheritance in such a
situation: “I’ll inherit from
Observable
to deal with mouse clicks, and I’ll ... er ... inherit from
Observable
to deal with mouse-overs, and, well, ... hmm, that doesn’t work.”
The
“interface” idiom
The
“inner class” idiom
Here’s
a situation where we do actually need to (in effect) upcast to more than one
type, but in this case we need to provide several
different
implementations of the same base type. The solution is something I’ve
lifted from Java, which takes C++’s nested class one step further. Java
has a built-in feature called
inner
classes
,
which look like C++’s nested classes, but they do two other things:
- A
Java inner class automatically has access to the private elements of the class
it is nested within.
- An
object of a Java inner class automatically grabs the “this” to the
outer class object it was created within. In Java, the “outer this”
is implicitly dereferenced whenever you name an element of the outer class.
So
to implement the inner class idiom in C++, we must do these things by hand.
Here’s an example:
//: C25:InnerClassIdiom.cpp
// Example of the "inner class" idiom
#include <iostream>
#include <string>
using namespace std;
class Poingable {
public:
virtual void poing() = 0;
};
void callPoing(Poingable& p) {
p.poing();
}
class Bingable {
public:
virtual void bing() = 0;
};
void callBing(Bingable& b) {
b.bing();
}
class Outer {
string name;
// Define one inner class:
class Inner1;
friend class Outer::Inner1;
class Inner1 : public Poingable {
Outer* parent;
public:
Inner1(Outer* p) : parent(p) {}
void poing() {
cout << "poing called for "
<< parent->name << endl;
// Accesses data in the outer class object
}
} inner1;
// Define a second inner class:
class Inner2;
friend class Outer::Inner2;
class Inner2 : public Bingable {
Outer* parent;
public:
Inner2(Outer* p) : parent(p) {}
void bing() {
cout << "bing called for "
<< parent->name << endl;
}
} inner2;
public:
Outer(const string& nm) : name(nm),
inner1(this), inner2(this) {}
// Return reference to interfaces
// implemented by the inner classes:
operator Poingable&() { return inner1; }
operator Bingable&() { return inner2; }
};
int main() {
Outer x("Ping Pong");
// Like upcasting to multiple base types!:
callPoing(x);
callBing(x);
} ///:~
The
example begins with the
Poingable
and
Bingable
interfaces,
each of which contain a single member function. The services provided by
callPoing( )
and
callBing( )
require
that the object they receive implement the
Poingable
and
Bingable
interfaces,
respectively, but they put no other requirements on that object so as to
maximize the flexibility of using
callPoing( )
and
callBing( ).
Note the lack of
virtual
destructors in either interface – the intent is that you never perform
object destruction via the interface.
Outer
contains some private data (
name)
and it wishes to provide both a
Poingable
interface and a
Bingable
interface
so it can be used with
callPoing( )
and
callBing( ).
Of course, in this situation we
could
simply use multiple inheritance. This example is just intended to show the
simplest syntax for the idiom; we’ll see a real use shortly. To provide a
Poingable
object without inheriting
Outer
from
Poingable,
the inner class idiom is used. First, the declaration
class
Inner
says that, somewhere, there is a nested class of this name. This allows the
friend
declaration for the class, which follows. Finally, now that the nested class
has been granted access to all the private elements of
Outer,
the class can be defined. Notice that it keeps a pointer to the
Outer
which created it, and this pointer must be initialized in the constructor.
Finally, the
poing( )
function from
Poingable
is implemented. The same process occurs for the second inner class which
implements
Bingable.
Each inner class has a single
private
instance created, which is initialized in the
Outer
constructor. By creating the member objects and returning references to them,
issues of object lifetime are eliminated.
Notice
that both inner class definitions are
private,
and in fact the client programmer doesn’t have any access to details of
the implementation, since the two access methods
operator
Poingable&( )
and
operator
Bingable&( )
only return a reference to the upcast interface, not to the object that
implements it. In fact, since the two inner classes are
private,
the client programmer cannot even downcast to the implementation classes, thus
providing complete isolation between interface and implementation.
Just
to push a point, I’ve taken the extra liberty here of defining the
automatic type conversion operators
operator
Poingable&( )
and
operator
Bingable&( )
.
In
main( ),
you can see that these actually allow a syntax that looks like
Outer
is multiply inherited from
Poingable
and
Bingable.
The difference is that the casts in this case are one way. You can get the
effect of an upcast to
Poingable
or
Bingable,
but you cannot downcast back to an
Outer.
In the following example of observer, you’ll see the more typical
approach: you provide access to the inner class objects using ordinary member
functions, not automatic type conversion operations.
The
observer example
Armed
with the
Observer
and
Observable
header
files and the inner class idiom, we can look at an example of the observer
pattern:
//: C25:ObservedFlower.cpp
// Demonstration of "observer" pattern
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include "Observable.h"
using namespace std;
class Flower {
bool isOpen;
public:
Flower() : isOpen(false),
openNotifier(this), closeNotifier(this) {}
void open() { // Opens its petals
isOpen = true;
openNotifier.notifyObservers();
closeNotifier.open();
}
void close() { // Closes its petals
isOpen = false;
closeNotifier.notifyObservers();
openNotifier.close();
}
// Using the "inner class" idiom:
class OpenNotifier;
friend class Flower::OpenNotifier;
class OpenNotifier : public Observable {
Flower* parent;
bool alreadyOpen;
public:
OpenNotifier(Flower* f) : parent(f),
alreadyOpen(false) {}
void notifyObservers(Argument* arg=0) {
if(parent->isOpen && !alreadyOpen) {
setChanged();
Observable::notifyObservers();
alreadyOpen = true;
}
}
void close() { alreadyOpen = false; }
} openNotifier;
class CloseNotifier;
friend class Flower::CloseNotifier;
class CloseNotifier : public Observable {
Flower* parent;
bool alreadyClosed;
public:
CloseNotifier(Flower* f) : parent(f),
alreadyClosed(false) {}
void notifyObservers(Argument* arg=0) {
if(!parent->isOpen && !alreadyClosed) {
setChanged();
Observable::notifyObservers();
alreadyClosed = true;
}
}
void open() { alreadyClosed = false; }
} closeNotifier;
};
class Bee {
string name;
// An "inner class" for observing openings:
class OpenObserver;
friend class Bee::OpenObserver;
class OpenObserver : public Observer {
Bee* parent;
public:
OpenObserver(Bee* b) : parent(b) {}
void update(Observable*, Argument *) {
cout << "Bee " << parent->name
<< "'s breakfast time!\n";
}
} openObsrv;
// Another "inner class" for closings:
class CloseObserver;
friend class Bee::CloseObserver;
class CloseObserver : public Observer {
Bee* parent;
public:
CloseObserver(Bee* b) : parent(b) {}
void update(Observable*, Argument *) {
cout << "Bee " << parent->name
<< "'s bed time!\n";
}
} closeObsrv;
public:
Bee(string nm) : name(nm),
openObsrv(this), closeObsrv(this) {}
Observer& openObserver() { return openObsrv; }
Observer& closeObserver() { return closeObsrv;}
};
class Hummingbird {
string name;
class OpenObserver;
friend class Hummingbird::OpenObserver;
class OpenObserver : public Observer {
Hummingbird* parent;
public:
OpenObserver(Hummingbird* h) : parent(h) {}
void update(Observable*, Argument *) {
cout << "Hummingbird " << parent->name
<< "'s breakfast time!\n";
}
} openObsrv;
class CloseObserver;
friend class Hummingbird::CloseObserver;
class CloseObserver : public Observer {
Hummingbird* parent;
public:
CloseObserver(Hummingbird* h) : parent(h) {}
void update(Observable*, Argument *) {
cout << "Hummingbird " << parent->name
<< "'s bed time!\n";
}
} closeObsrv;
public:
Hummingbird(string nm) : name(nm),
openObsrv(this), closeObsrv(this) {}
Observer& openObserver() { return openObsrv; }
Observer& closeObserver() { return closeObsrv;}
};
int main() {
Flower f;
Bee ba("A"), bb("B");
Hummingbird ha("A"), hb("B");
f.openNotifier.addObserver(ha.openObserver());
f.openNotifier.addObserver(hb.openObserver());
f.openNotifier.addObserver(ba.openObserver());
f.openNotifier.addObserver(bb.openObserver());
f.closeNotifier.addObserver(ha.closeObserver());
f.closeNotifier.addObserver(hb.closeObserver());
f.closeNotifier.addObserver(ba.closeObserver());
f.closeNotifier.addObserver(bb.closeObserver());
// Hummingbird B decides to sleep in:
f.openNotifier.deleteObserver(hb.openObserver());
// Something changes that interests observers:
f.open();
f.open(); // It's already open, no change.
// Bee A doesn't want to go to bed:
f.closeNotifier.deleteObserver(
ba.closeObserver());
f.close();
f.close(); // It's already closed; no change
f.openNotifier.deleteObservers();
f.open();
f.close();
} ///:~
The
events of interest are that a
Flower
can open or close. Because of the use of the inner class idiom, both these
events can be separately-observable phenomena.
OpenNotifier
and
CloseNotifier
both inherit
Observable,
so they have access to
setChanged( )
and can be handed to anything that needs an
Observable.
You’ll notice that, contrary to
InnerClassIdiom.cpp,
the
Observable
descendants are
public.
This is because some of their member functions must be available to the client
programmer. There’s nothing that says that an inner class must be
private;
in
InnerClassIdiom.cpp
I was simply following the design guideline “make things as private as
possible.” You could make the classes
private
and expose the appropriate methods by proxy in
Flower,
but it wouldn’t gain much.
The
inner class idiom also comes in handy to define more than one kind of
Observer,
in
Bee
and
Hummingbird,
since both those classes may want to independently observe
Flower
openings and closings. Notice how the inner class idiom provides something that
has most of the benefits of inheritance (the ability to access the private data
in the outer class, for example) without the same restrictions.
In
main( ),
you can see one of the prime benefits of the observer pattern: the ability to
change behavior at run-time by dynamically registering and un-registering
Observers
with
Observables.
If
you study the code above you’ll see that
OpenNotifier
and
CloseNotifier
use the basic
Observable
interface. This means that you could inherit other completely different
Observer
classes; the only connection the
Observers
have with
Flowers
is the
Observer
interface.
Go to CodeGuru.com
Contact: webmaster@codeguru.com
© Copyright 1997-1999 CodeGuru