Callbacks
Functor
Command
Strategy
Observer
Likethe other forms of callback: a hook point where you can change code. Differenceis in observer’s completely dynamic nature. Often used for the specificcase of changes based on other object’s change of state, but is also thebasis of event management. Anytime you want to decouple the source of the callfrom the called code in a completely dynamic way.
Theobserverpattern solves a fairly common problem: What if a group of objects needs toupdate 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-ViewArchitecture.” 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, andthat’s what the observer facilitates.
Thereare two types of objects used to implement the observer pattern in thefollowing 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
.
Thereare actually two “things that change” in the observer pattern: thequantity of observing objects and the way an update occurs. That is, theobserver pattern allows you to modify both of these without affecting thesurrounding code.
Thereare a number of ways to implement the observer pattern, but the code shown herewill create a framework from which you can build your own observer code,following the example. First, this interface describes what an observer lookslike:
//: 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 wishto pass during an update. If you want, you can simply pass the extra argumentas 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 thatobject decides its time to update all it’s observers. The arguments areoptional; you could have an
update( )
with no arguments and that would still fit the observer pattern; however thisis more general – it allows the observed object to pass the object thatcaused the update (since an
Observer
maybe registered with more than one observed object) and any extra information ifthat’s helpful, rather than forcing the
Observer
object to hunt around to see who is updating and to fetch any other informationit needs.
The“observed object” that decides when and how to do the updating willbe 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 away to register an
Observer
with an
Observable
and for the
Observable
to update its
Observer
s,the set of member functions doesn’t matter. However, this design isintended to be reusable (it was lifted from the design used in the Javastandard library). As mentioned elsewhere in the book, there is no support formultithreading in the Standard C++ libraries, so this design would need to bemodified 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. Theflag allows you to wait, and only notify the
Observer
swhen you decide the time is right. Notice, however, that the control of theflag’s state is
protected
,so that only an inheritor can decide what constitutes a change, and not the enduser of the resulting derived
Observer
class.
Thecollection of
Observer
objects is kept in a
set<Observer*>
to prevent duplicates; the
setinsert( )
,
erase( )
,
clear( )
and
size( )
functions are exposed to allow
Observer
sto be added and removed at any time, thus providing run-time flexibility.
Mostof 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.
Atfirst 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, whichmeans 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.
Nowwe encounter a dilemma. An object that should notify its observers about thingsthat happen to it – events or changes in state – might have morethan one such item of interest. For example, if you’re dealing with agraphical user interface (GUI) item – a button, say – the items ofinterest might be the mouse clicked the button, the mouse moved over thebutton, and (for some reason) the button changed its color. So we’d liketo be able to report all of these events to different observers, each of whichis interested in a different type of event.
Theproblem is that we would normally reach for multiple inheritance in such asituation: “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’sa situation where we do actually need to (in effect) upcast to more than onetype, but in this case we need to provide several
different
implementations of the same base type. The solution is something I’velifted from Java, which takes C++’s nested class one step further. Javahas a built-in feature called
innerclasses
,which look like C++’s nested classes, but they do two other things:
- AJava inner class automatically has access to the private elements of the classit is nested within.
- Anobject of a Java inner class automatically grabs the “this” to theouter class object it was created within. In Java, the “outer this”is implicitly dereferenced whenever you name an element of the outer class.
Soto 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); } ///:~
Theexample begins with the
Poingable
and
Bingable
interfaces,each of which contain a single member function. The services provided by
callPoing( )
and
callBing( )
requirethat the object they receive implement the
Poingable
and
Bingable
interfaces,respectively, but they put no other requirements on that object so as tomaximize the flexibility of using
callPoing( )
and
callBing( )
.Note the lack of
virtual
destructors in either interface – the intent is that you never performobject destruction via the interface.
Outer
contains some private data (
name
)and it wishes to provide both a
Poingable
interface and a
Bingable
interfaceso 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 thesimplest 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
classInner
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 classhas 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 whichimplements
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.
Noticethat both inner class definitions are
private
,and in fact the client programmer doesn’t have any access to details ofthe implementation, since the two access methods
operatorPoingable&( )
and
operatorBingable&( )
only return a reference to the upcast interface, not to the object thatimplements it. In fact, since the two inner classes are
private
,the client programmer cannot even downcast to the implementation classes, thusproviding complete isolation between interface and implementation.
Justto push a point, I’ve taken the extra liberty here of defining theautomatic type conversion operators
operatorPoingable&( )
and
operatorBingable&( )
.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 theeffect 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 typicalapproach: you provide access to the inner class objects using ordinary memberfunctions, not automatic type conversion operations.
Theobserver example
Armedwith the
Observer
and
Observable
headerfiles and the inner class idiom, we can look at an example of the observerpattern:
//: 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(); } ///:~
Theevents of interest are that a
Flower
can open or close. Because of the use of the inner class idiom, both theseevents 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 clientprogrammer. 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 aspossible.” You could make the classes
private
and expose the appropriate methods by proxy in
Flower
,but it wouldn’t gain much.
Theinner 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 thathas most of the benefits of inheritance (the ability to access the private datain 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 tochange behavior at run-time by dynamically registering and un-registering
Observer
swith
Observable
s.
Ifyou 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
Observer
shave with
Flower
sis the
Observer
interface.