Observer - shoonie/StudyingDesignPattern GitHub Wiki

Intent

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Diagram

disgram

Consequences

  1. Abstract coupling between Subject and Observer.
  2. Support for broadcast communication.
  3. Unexpected updates.

Implementation

  1. Mapping subjects to their observers.
  2. Observing more than one subject.
  3. Who triggers the update?
  • Have state-setting operations on Subject call Notify after they change the subject's state.
  • Make clients responsible for calling Notify at the right time.
  1. Dangling references to deleted subjects.
  2. Making sure Subject state is self-consistent before notification.

Sample Code

#include <iostream>
#include <vector>
#include <time.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <string.h>

using namespace std ; 

class Subject; 

class Observer 
{ 
public: 
 Observer() {}; 
 ~Observer() {}; 
 virtual void Update(Subject* theChangeSubject) = 0; 
}; 

class Subject 
{ 
public: 
 Subject() {}; 
 virtual ~Subject() {}; 
 virtual void Attach(Observer*); 
 virtual void Detach(Observer*); 
 virtual void Notify();  
private: 
 vector<Observer*> _observers; 
}; 

void Subject::Attach (Observer* o) 
{ 
 _observers.push_back(o); 
} 

void Subject::Detach (Observer* o) 
{ 
 int count = _observers.size(); 
 int i; 

 for (i = 0; i < count; i++) { 
   if(_observers[i] == o) 
   break; 
 } 
 if(i < count) 
  _observers.erase(_observers.begin() + i); 

} 

void Subject::Notify () 
{ 
 int count = _observers.size(); 

 for (int i = 0; i < count; i++) 
   (_observers[i])->Update(this); 
}

class ClockTimer : public Subject 
{ 
public: 
 ClockTimer() { _strtime( tmpbuf ); }; 
 int GetHour(); 
 int GetMinute();
 int GetSecond(); 
 void Tick();   
private: 
 char tmpbuf[128]; 
}; 

 /* Set time zone from TZ environment variable. If TZ is not set, 
  * the operating system is queried to obtain the default value 
  * for the variable. 
 */ 
void ClockTimer::Tick() 
{ 
    _tzset(); 

// Obtain operating system-style time. 
    _strtime( tmpbuf ); 
    Notify(); 
} 

int ClockTimer::GetHour() 
{ 
 char timebuf[128]; 
 strncpy(timebuf, tmpbuf, 2); 
 timebuf[2] = NULL; 
  
 return atoi(timebuf); 
} 

int ClockTimer::GetMinute() 
{ 
 char timebuf[128]; 
 strncpy(timebuf, tmpbuf+3, 2); 
 timebuf[2] = NULL; 

 return atoi(timebuf); 
} 

int ClockTimer::GetSecond() 
{ 
 char timebuf[128];
 strncpy(timebuf, tmpbuf+6, 2); 
 timebuf[2] = NULL; 

 return atoi(timebuf); 
}


class DigitalClock: public Observer 
{ 
public: 
 DigitalClock(ClockTimer *);  
 ~DigitalClock();   
  void Update(Subject *);   
  void Draw();     
private: 
 ClockTimer *_subject;  
}; 

DigitalClock::DigitalClock (ClockTimer *s) 
{ 
 _subject = s; 
 _subject->Attach(this); 
} 

DigitalClock::~DigitalClock () 
{ 
 _subject->Detach(this); 
} 

void DigitalClock::Update (Subject *theChangedSubject) 
{ 
 if(theChangedSubject == _subject) 
  Draw(); 
} 

void DigitalClock::Draw () 
{ 
 int hour = _subject->GetHour(); 
 int minute = _subject->GetMinute(); 
 int second = _subject->GetSecond(); 

 cout << "Digital time is " << hour << ":" 
          << minute << ":" 
          << second << endl;           
}

class AnalogClock: public Observer 
{ 
public: 
 AnalogClock(ClockTimer *);  
 ~AnalogClock();    
  void Update(Subject *);  
  void Draw();     
private: 
 ClockTimer *_subject;   
}; 

AnalogClock::AnalogClock (ClockTimer *s) 
{ 
 _subject = s; 
 _subject->Attach(this); 
} 

AnalogClock::~AnalogClock () 
{ 
 _subject->Detach(this); 
} 

void AnalogClock::Update (Subject *theChangedSubject) 
{ 
 if(theChangedSubject == _subject) 
  Draw(); 
} 

void AnalogClock::Draw () 
{ 
 int hour = _subject->GetHour(); 
 int minute = _subject->GetMinute(); 
 int second = _subject->GetSecond(); 

 cout << "Analog time is " << hour << ":" 
         << minute << ":" 
         << second << endl; 
}

int main(void) 
{ 
 ClockTimer timer; 

 DigitalClock digitalClock(&timer;); 
 AnalogClock analogClock(&timer;); 
  
 timer.Tick();  
  
 return 0; 
}
⚠️ **GitHub.com Fallback** ⚠️