Observer Pattern - wie funktioniert das eigentlich?
-
Observer Pattern - wie funktioniert das eigentlich?
-
Einfaches Beispiel (basiert auf vereinfachtem Code von Gamma et.al. http://www.cs.clemson.edu/~malloy/courses/patterns/observerEx.html):
#include "stdafx.h" #include <deque> #include <ctime> #include <cstring> #include <cstdlib> #include <string> #include <iostream> using namespace std ; /************************** Subjekt und Beobachter ***********************************/ class Subject; struct Observer { virtual void Update( Subject* ) = 0; }; class Subject { public: void Attach( Observer* ); void Detach( Observer* ); void Notify(); private: deque<Observer*> observers_; }; void Subject::Attach( Observer* o ) //MeldeAn { observers_.push_back(o); } void Subject::Detach( Observer* o ) //MeldeAb { size_t n = observers_.size(); size_t i; for( i = 0; i < n; ++i ) { if(observers_[i] == o) break; } if(i < n) observers_.erase( observers_.begin() + i ); } void Subject::Notify() //Benachrichtige { size_t n = observers_.size(); for( size_t i = 0; i < n; ++i ) observers_[i]->Update(this); } /************************** Konkretes Subjekt ***********************************/ class Timer : public Subject { public: Timer() { _strtime_s( buffer_ ); }; std::string Timer::GetTime()const {return std::string(buffer_);} void Tick(); private: char buffer_[128]; }; void Timer::Tick() { _tzset(); _strtime_s( buffer_ ); Notify(); } /************************** Konkreter Beobachter ***********************************/ class Clock: public Observer { public: Clock( std::string name, Timer* ); ~Clock(); void Update( Subject* ); std::string getName() const {return name_;} Timer* getSubject() const {return subject_;} private: std::string name_; Timer* subject_; }; Clock::Clock( std::string name, Timer* s ) : subject_(s) { name_ = name; subject_->Attach(this); } Clock::~Clock () { subject_->Detach(this); } void Clock::Update( Subject* s ) //Aktualisiere { if( s == subject_ ) cout << getName() << " zeigt " << subject_->GetTime() << endl; } /************************** Hauptprogramm ***********************************/ int main() { Timer t; //Subjekt const int N = 100; char buffer[20]; Clock* pC[N]; for(int i=0; i<N; ++i) { _itoa_s( i, buffer, 10 ); pC[i] = new Clock( buffer, &t ); //Observer } t.Tick(); wait(); for(int i=0; i<N/2; ++i) { pC[i]->getSubject()->Detach(pC[i]); //Einige Observer melden sich ab } t.Tick(); wait(); for(int i=0; i<N; ++i) { delete pC[i]; } return 0; }Ein gutes Beispiel ist auch ein MailServer und entsprechene EmailKonten:
#include "stdafx.h" #include <deque> #include <string> #include <ostream> /************************** Subjekt und Beobachter ***********************************/ class Subject; struct Observer { virtual void update( Subject* ) = 0; }; class Subject { public: void attach( Observer* ); void detach( Observer* ); void notify(); private: std::deque<Observer*> observers_; }; void Subject::attach( Observer* o ) //MeldeAn { observers_.push_back(o); } void Subject::detach( Observer* o ) //MeldeAb { size_t n = observers_.size(); size_t i; for( i = 0; i < n; ++i ) { if(observers_[i] == o) break; } if(i < n) observers_.erase( observers_.begin() + i ); } void Subject::notify() //Benachrichtige { size_t n = observers_.size(); for( size_t i = 0; i < n; ++i ) observers_[i]->update(this); } /************************** Konkretes Subjekt ***********************************/ class MailServer : public Subject { public: MailServer( std::string name ): name_(name){} void neuerNewsletter() { notify(); } std::string getName() const {return name_;} private: const std::string name_; }; /************************** Konkreter Beobachter ***********************************/ class EmailKonto: public Observer { public: EmailKonto( std::string name, MailServer* ); ~EmailKonto(); void update( Subject* ); std::string getName() const {return name_;} MailServer* getSubject() const {return subject_;} private: const std::string name_; MailServer* subject_; }; EmailKonto::EmailKonto( std::string name, MailServer* s ) : name_(name), subject_(s) { subject_->attach(this); } EmailKonto::~EmailKonto () { subject_->detach(this); } void EmailKonto::update( Subject* s ) //Aktualisiere { if( s == subject_ ) std::cout << "Hallo Herr Nr. " << getName() << ", ein neuer Newsletter von " << subject_->getName()<< " ist erschienen." << std::endl; } /************************** Hauptprogramm ***********************************/ int main() { MailServer s("HenkesSoft3000"); //Subjekt "MailServer" const int N = 5; char buffer[10]; EmailKonto* pMK[N]; for(int i=0; i<N; ++i) { _itoa_s( i, buffer, 10 ); pMK[i] = new EmailKonto( buffer, &s ); //Mailkonten melden sich an } s.neuerNewsletter(); wait(); for(int i=0; i<(N-2); ++i) { pMK[i]->getSubject()->detach(pMK[i]); //Zwei Mailkonten melden sich ab } s.neuerNewsletter(); wait(); for(int i=0; i<N; ++i) { delete pMK[i]; } return 0; }
-
Hier ist auch noch ein gutes Beispiel:
http://www.c-plusplus.net/forum/viewtopic-var-t-is-147713-and-highlight-is-*observer*.html
-
c780t
-
Eine gute Erklärung bietet auch unser Artikel zu Design Patterns:
http://magazin.c-plusplus.net/artikel/Einf%FChrung%20in%20Design%20Patterns
-
ok ohne mir annährend alles durchzulesen
die ganzen actionlistener, eventlistener usw in java sind im grunde ja einfach observer patterns oder?
-
Hallo!
Ich hatte mal Observer Pattern in einer anderen Programmiersprache implementiert. Es funktioniert bei mir ungefähr so...
Dispatcher ist ein getrenntes Programm, das auf einem Webserver installiert ist. Dispatcher führt eine Tabelle mit Clients und Topics. Sobald sich ein neuer Client anmeldet wird dieser in die Tabelle eingetragen. Danach sagt der Client welche Topics von Interesse sind, der Dispatcher trägt es in die Tabelle ein. Die Tabelle sieht ungefähr so aus:
Topic1 Topic2 Topic3
Client1 0 1 0
Client2 1 1 1
Client3 1 0 1Es bedeutet dass Client1 nur Topic2 emfangen will, Client2 alle drei Topics und Client3 nur Topic1 und Topic3.
Sobald der Dispatcher eine Nachricht von einem Client empfangt (Topic wird in der Nachricht mitgeteilt) schaut der Dispatcher in der Tabelle nach, welche Clients sich nun für diese Nachricht interessieren und schickt diese Nachricht nacheinander zu den Clients ab.
Und nun wollte ich den Dispatcher auf C++ Basis umprogrammieren, weil die Exe dann direkt ohne weiteres auf dem Server gestartet werden kann. In LabVIEW sollte man zuerst eine Runtime installieren.
Deshalb soll das Dispatcher-Prog komplett getrennt von Clients programmiert werden. Die Kommunikation soll ausschliesslich über TCP/IP laufen. Gibt es dazu irgendwelche Beispiele?
Mfg, labviewer