Observer Pattern

Observer gibt eine Lösung vor für gängiges und wichtiges Problem: Praktisches Beispiel: Spreadsheet/Tabellenkalkulation

Auch Signal-Slot Mechanismus behandelt dieses Problem. Qt Signal-Slot mögliche Lösung, allerdings Erweiterung von C++ nötig.


Das Observer Pattern gibt vor, wie solche Abhängigkeiten zwischen verschiedenen Klassen/Objekten implementiert werden:
Observer - Class Diagram


Image ObsClassObserver - Sequenz Diagram


Image ObsSeqObserver in C++



class Subject;
// Observer: just an interface
class Observer {
public:
     Observer(){};
     virtual ~Observer(){};
     virtual void update(Subject* ) = 0; 
};
class Subject {
public:
  Subject();
  virtual ~Subject(){};
  virtual void attach(Observer*);
  virtual void detach(Observer*);
  virtual void notify();
  virtual void setChanged();
private:
  list<Observer*> observers; // list to hold observers
  bool changed;
};

void Subject::setChanged() { 
  changed = true ;
}

Subject::Subject() : changed(false) {}
  
void Subject::attach (Observer* o) {
  observers.push_back( o); 
}

void Subject::detach (Observer* o) {
  observers.remove(o); 
}

void Subject::notify () {
  // notify observers if changed flag is set
  if ( changed ) {
    list<Observer*>::iterator i = observers.begin();
    for ( ; i!= observers.end(); i++ ) {
      (*i)->update(this);
    }
    changed = false;
  }
}



Beispiel: Temperatur-Sensor Simulation



#include <list>
#include <iterator>
#include <iostream>
using namespace std;

// real classes using the Observer 
class Sensor : public Subject { // simulates temperature sensor
private:
  int temp; 
public:
  Sensor();
  void takeReading(); 
  int getReading(); 
}; 

Sensor::Sensor() {
  temp = 20; // set some start value
}

void Sensor::takeReading() { 
  double d;  // randomly change temperature in 50% of times
  d = rand()*1./(RAND_MAX+1.0);
  if(d>0.75) { 
    temp++; 
    setChanged(); 
  } else if (d<0.25) { 
    temp--; 
    setChanged(); 
  } 
  cout << " [Temp: " << temp  << "] "; 
} 
int Sensor::getReading() { 
  return temp; 
}

class Display : public Observer { 
public:
  virtual void update(Subject * o); 
};
void Display::update(Subject * o) { 
  Sensor * s = dynamic_cast< Sensor *>(o);
  if ( s ) {
    cout << "New Temp: " << s->getReading(); 
  }
} 

int main()
{
  Sensor *sensor = new Sensor();   // subject
  Display *display = new Display(); // Observer
  // register observer with observable class 
  sensor->attach(display); // Simulate measuring temp over time 
  for(int i=0; i < 20; i++) { 
    sensor->takeReading(); 
    sensor->notify(); 
    cout << endl;
  }
  return 0;
}



Im Beispiel wird notify() von aussen (=main()) angestossen. Kann aber genauso direkt vom Subject Sensor gemacht werden.


GDuckeck 2019-08-01