// Purpose. Observer class Obs; // // Discussion. On the left: Subject class Subject { // has hard-wired the number and type public: // of "dependent" objects, the compil- Subject(); // er must have the declaration of the void attach( Obs* ); // concrete Obs classes to compile void setVal( int ); // Subject's decl, the Obs classes ex- int getVal(); // ercise no reuse of i/f or impl, and void notify(); // Subject "pushes" updates to the Obs private: // objects. On the right: Subject is Obs* obs_[10]; // decoupled from the number, type, int num_, val_; // and declaration of concrete Obs }; // subclasses; the Obs objects accept // polymorphism; and Subject broad- class Obs { public: // casts that a change has occurred virtual void update() = 0; // followed by Obs objects requesting protected: // just the info that they want. Subject* sub_; int div_; class DivObs { public: }; DivObs( int ); class DivObs : public Obs { public: void update( int ); DivObs( Subject*, int ); private: void update(); int div_; }; }; class ModObs : public Obs { public: class ModObs { public: ModObs( Subject*, int ); ModObs( int ); void update(); void update( int ); }; private: int div_; Subject::Subject() { num_ = 0; } }; int Subject::getVal() { return val_; } void Subject::attach( Obs* o ) { class Subject { obs_[num_++] = o; } public: void Subject::setVal( int v ) { Subject(); val_ = v; notify(); } void setVal( int ); void Subject::notify() { private: for (int i=0; i < num_; i++) int val_; obs_[i]->update(); } DivObs div_; ModObs mod_; DivObs::DivObs( Subject* s, int d ) { }; sub_ = s; div_ = d; sub_->attach( this ); } Subject::Subject() : div_(4), mod_(3) void DivObs::update() { { } int v = sub_->getVal(); void Subject::setVal( int v ) { cout << v << " div " << div_ val_ = v; << " is " << v / div_ << endl; } div_.update( val_ ); ModObs::ModObs( Subject* s, int d ) { mod_.update( val_ ); } sub_ = s; div_ = d; sub_->attach( this ); } DivObs::DivObs( int d ) { div_ = d; } void ModObs::update() { void DivObs::update( int v ) { int v = sub_->getVal(); cout << v << " div " << div_ cout << v << " mod " << div_ << " is " << v / div_ << endl; } << " is " << v % div_ << endl; } ModObs::ModObs( int d ) { div_ = d; } void ModObs::update( int v ) { void main( void ) cout << v << " mod " << div_ { << " is " << v % div_ << endl; } Subject subj; DivObs divObs1( &subj, 4 ); void main( void ) DivObs divObs2( &subj, 3 ); { ModObs modObs3( &subj, 3 ); Subject subj; subj.setVal( 14 ); subj.setVal( 14 ); } } // 14 div 4 is 3 // 14 div 4 is 3 // 14 div 3 is 4 // 14 mod 3 is 2 // 14 mod 3 is 2 // Purpose. Observer design pattern // 1. Model the "independent" functionality with a "subject" abstraction // 2. Model the "dependent" functionality with "observer" hierarchy // 3. The Subject is coupled only to the Observer base class // 4. Observers register themselves with the Subject // 5. The Subject broadcasts events to all registered Observers // 6. Observers "pull" the information they need from the Subject // 7. Client configures the number and type of Observers #include #include using namespace std; class Subject { // 1. "independent" functionality vector views; // 3. Coupled only to "interface" int value; public: void attach( Observer* obs ) { views.push_back( obs ); } void setVal( int val ) { value = val; notify(); } int getVal() { return value; } void notify(); }; class Observer { // 2. "dependent" functionality Subject* model; int denom; public: Observer( Subject* mod, int div ) { model = mod; denom = div; // 4. Observers register themselves with the Subject model->attach( this ); } virtual void update() = 0; protected: Subject* getSubject() { return model; } int getDivisor() { return denom; } }; void Subject::notify() { // 5. Publisher broadcasts for (int i=0; i < views.size(); i++) views[i]->update(); } class DivObserver : public Observer { public: DivObserver( Subject* mod, int div ) : Observer(mod,div) { } void update() { // 6. "Pull" information of interest int v = getSubject()->getVal(), d = getDivisor(); cout << v << " div " << d << " is " << v / d << '\n'; } }; class ModObserver : public Observer { public: ModObserver( Subject* mod, int div ) : Observer(mod,div) { } void update() { int v = getSubject()->getVal(), d = getDivisor(); cout << v << " mod " << d << " is " << v % d << '\n'; } }; void main( void ) { Subject subj; DivObserver divObs1( &subj,4 ); // 7. Client configures the number and DivObserver divObs2( &subj,3 ); // type of Observers ModObserver modObs3( &subj,3 ); subj.setVal( 14 ); } // 14 div 4 is 3 // 14 div 3 is 4 // 14 mod 3 is 2 // Purpose. Observer design pattern, class inheritance vs type inheritance // SensorSystem is the "subject". Lighting, Gates, and Surveillance are the // "views". The subject is only coupled to the "abstraction" of AlarmListener. // An object's class defines how the object is implemented. In contrast, an // object's type only refers to its interface. Class inheritance defines an // object's implementation in terms of another object's implementation. Type // inheritance describes when an object can be used in place of another. // [GoF, pp13-17] #include #include using namespace std; class AlarmListener { public: virtual void alarm() = 0; }; class SensorSystem { vector listeners; public: void attach( AlarmListener* al ) { listeners.push_back( al ); } void soundTheAlarm() { for (int i=0; i < listeners.size(); i++) listeners[i]->alarm(); } }; class Lighting : public AlarmListener { public: /*virtual*/ void alarm() { cout << "lights up" << '\n'; } }; class Gates : public AlarmListener { public: /*virtual*/ void alarm() { cout << "gates close" << '\n'; } }; class CheckList { virtual void localize() { cout << " establish a perimeter" << '\n'; } virtual void isolate() { cout << " isolate the grid" << '\n'; } virtual void identify() { cout << " identify the source" << '\n'; } public: void byTheNumbers() { // Template Method design pattern localize(); isolate(); identify(); } }; // class inheri. // type inheritance class Surveillance : public CheckList, public AlarmListener { /*virtual*/ void isolate() { cout << " train the cameras" << '\n'; } public: /*virtual*/ void alarm() { cout << "Surveillance - by the numbers:" << '\n'; byTheNumbers(); } }; void main( void ) { SensorSystem ss; ss.attach( &Gates() ); ss.attach( &Lighting() ); ss.attach( &Surveillance() ); ss.soundTheAlarm(); } // gates close // lights up // Surveillance - by the numbers: // establish a perimeter // train the cameras // identify the source // Purpose. Observer and Mediator demo // // Observer - 1. Sender is coupled to a Receiver interface // 2. Receivers register with Sender // 3. Sender broadcasts (in the blind) to all Receivers // // Mediator - 4. Sender(s) has handle to Mediator // 5. Mediator has handle to Receiver(s) // 6. Sender(s) sends to Mediator // 7. Mediator sends to Receiver(s) #include #include using namespace std; void gotoxy( int, int ); class Observer { public: virtual void update( int ) = 0; }; class Mediator { vector groups[3]; // 1. Sender is coupled to an interface public: enum Message { ODD, EVEN, THREE }; // 1. Sender is coupled to an interface void attach( Observer* o, Message type ) { groups[type].push_back( o ); } void disseminate( int num ) { if (num % 2 == 1) // odd // 3,7. Sender/Mediator broadcasts for (int i=0; i < groups[0].size(); i++) groups[0][i]->update(num); else // even for (int i=0; i < groups[1].size(); i++) groups[1][i]->update(num); if (num % 3 == 0) // /3 for (int i=0; i < groups[2].size(); i++) groups[2][i]->update(num); } }; class OddObserver : public Observer { int col, row; public: OddObserver( Mediator& med, int c ) { col = c; row = 3; gotoxy( col, 1 ); cout << "Odd"; gotoxy( col, 2 ); cout << "---"; med.attach( this, Mediator::ODD ); // 2,5. Receivers register with Sender } void update( int num ) { gotoxy( col, row++ ); cout << num; } }; class EvenObserver : public Observer { int col, row; public: EvenObserver( Mediator& med, int c ) { col = c; row = 3; gotoxy( col, 1 ); cout << "/2"; gotoxy( col, 2 ); cout << "--"; med.attach( this, Mediator::EVEN ); } void update( int num ) { gotoxy( col, row++ ); cout << num; } }; class ThreeObserver : public Observer { int col, row; public: ThreeObserver( Mediator& med, int c ) { col = c; row = 3; gotoxy( col, 1 ); cout << "/3"; gotoxy( col, 2 ); cout << "--"; med.attach( this, Mediator::THREE ); } void update( int num ) { gotoxy( col, row++ ); cout << num; } }; class Publisher { public: // 6. Sender sends to Mediator Publisher( Mediator& med ) { for (int i=1; i < 10; i++) med.disseminate(i); } }; void main( void ) { Mediator mediator; OddObserver( mediator, 1 ); EvenObserver( mediator, 11 ); ThreeObserver( mediator, 21 ); Publisher producer( mediator ); // 4. Sender has handle to Mediator } // Odd /2 /3 // --- -- -- // 1 2 3 // 3 4 6 // 5 6 9 // 7 8 // 9 // Purpose. TypedMessage - embellished Observer, decoupled messaging // // Messages inherit from TypedMessage // Message listeners inherit from many Message::Handlers // Application tells message to publish/broadcast/notify // Messages are the subject (receive registrations from subscribers) // Subsystems are the observers (receive broadcast messages) // TypedMessage accomodates everything: registration, containment, and // notification of observers #include #include #include using namespace std; template class TypedMessage { static vector registry; public: class Handler { public: Handler() { TypedMessage::registerHandler( this ); } virtual void handleEvent( const T* t ) = 0; }; void notify() { for (int i=0; i < registry.size(); i++) registry.at(i)->handleEvent( (T*)this ); } static void registerHandler( Handler* h ) { registry.push_back( h ); } }; class On : public TypedMessage { string comment; public: On( string str ) { comment = str; } void start() const { cout << "OnEvent.start - " << comment << '\n'; } }; vector::Handler*> TypedMessage::registry; class Off : public TypedMessage { string comment; public: Off( string str ) { comment = str; } void stop() const { cout << "OffEvent.stop - " << comment << '\n'; } }; vector::Handler*> TypedMessage::registry; class MasterConsole : public On::Handler, public Off::Handler { public: void handleEvent( const On* msg ) { cout << "MasterConsole - "; msg->start(); } void handleEvent( const Off* msg ) { cout << "MasterConsole - "; msg->stop(); } }; class PowerMonitor : public On::Handler { public: void handleEvent( const On* msg ) { cout << "PowerMonitor - "; msg->start(); } }; void main( void ) { MasterConsole mc; PowerMonitor pm; On oneEvent( "lights" ); Off thrEvent( "elevators" ); On twoEvent( "hvac" ); Off fouEvent( "frontDoor" ); oneEvent.notify(); twoEvent.notify(); thrEvent.notify(); fouEvent.notify(); } // MasterConsole - OnEvent.start - lights // PowerMonitor - OnEvent.start - lights // MasterConsole - OnEvent.start - hvac // PowerMonitor - OnEvent.start - hvac // MasterConsole - OffEvent.stop - elevators // MasterConsole - OffEvent.stop - frontDoor