Często się zdarza że programiści napotykają problem we współczesnych programu posiadające interfejs użytkownika (GUI), muszą reagować na zmiany danych w kilku różnych miejscach jednocześnie. Do sytuacji takiej może dojść wtedy gdy pracujemy na tych samych danych, które możemy edytować w kilku miejscach jednocześnie np. wykresy obrazujące takie same dane. Zmiana wartości na jednym wykresie implikuje zmianę wartości na pozostałych wykresach.
W przypadku kiedy mamy do czynienia z kilkoma obiektami możemy wykorzystać mechanizm sygnałów i slotów w Qt, jednak jeżeli jednak liczba obserwowanych obiektów, które się zmieniają nie jest zdefiniowana lub dotyczy znacznej grupy obiektów możemy zbudować osobną klasę zwaną Obserwatorem mającą na celu powiadamianie obiektów o występujących zmianach.
Pomimo że istnieje wiele różnych implementacji tego samego wzorca mają one elementy wspólne takie jak:
Wszystkie z obiektów można podzielić na dwie osobne grupy wydawców i klas pełniących rolę obserwatorów
Każda z implementacji zakłada że jest jeden obiekt, które informuje inne obiekty o zmianach
Mechanizm wysyłania informacji do pozostałych klas jest zdefiniowany w całości w klasie bazowej wydawcy.
Zalety i wady
Zalety:
Luźna zależność między obiektem obserwującym i obserwowanym. Ponieważ nie wiedzą one wiele o sobie nawzajem, mogą być niezależnie rozszerzane i rozbudowywane bez wpływu na drugą stronę.
Relacja między obiektem obserwowanym a obserwatorem tworzona jest podczas wykonywania programu i może być dynamicznie zmieniana.
Możliwość zablokowania klientowi drogi do bezpośredniego korzystania ze złożonego systemu, jeśli jest to konieczne.
Wady:
Obserwatorzy nie znają innych obserwatorów, co w pewnych sytuacjach może wywołać trudne do znalezienia skutki uboczne.
Zastosowanie
Wzorzec Obserwatora sprawdza się wszędzie tam, gdzie stan jednego obiektu uzależniony jest od stanu drugiego obiektu.
Implementacja
class Obserwator {
public:
virtual void update() = 0;
};
//Obserwowany:
class Obserwowany {
protected:
std::list <obserwator*> obserwatorzy;
public:
void dodaj(Obserwator *o) {
obserwatorzy.push_back (o);
}
void usun(Obserwator *o) {
obserwatorzy.remove (o);
}</obserwator*>
void powiadom () {
std::list<obserwator *="">::iterator it;
for (it = obserwatorzy.begin(); it != obserwatorzy.end(); it++) {
(*it)->update ();
}
}
};
Obserwowany Konkretny:
Dziedziczenie wielokrotne dostępne w C++ jest często używane w wzorcu Obserwatora
protected:
int ilosc;
public:
Miod (int q) {
ilosc = q;
}
int pobierzIlosc () {
return ilosc;
}
void ustawIlosc (int q) {
ilosc = q;
}
};
class ObserwowanyMiod : public Obserwowany, public Miod {
std::string stan;
public:
ObserwowanyMiod (int q) : Miod(q) {
}
std::string pobierzStan () {
return stan;
}
void ustawStan (const std::string& s) {
stan = s;
std::cout << "Stan: " << stan << std::endl;
powiadom();
}
};
Obserwator Konkretny:
protected:
int misId;
ObserwowanyMiod *miod;
public:
ObserwatorMis (ObserwowanyMiod *h, int id) {
miod = h;
misId = id;
}
void update () {
std::string stan = miod->pobierzStan();
if (!stan.compare ("Sa ludzie blisko miodu")) {
std::cout << "Mis" << misId << ": Czekam w ukryciu" << std::endl;
} else if (!stan.compare ("Nie ma ludzi blisko miodu")) {
std::cout << "Mis" << misId << ": Kradne miod" << std::endl;
miod->ustawIlosc(miod->pobierzIlosc()-1);
} else if (!stan.compare ("Ida ludzie")) {
std::cout << "Mis" << misId << ": Uciekam" << std::endl;
}
}
};
Przykład użycia:
ObserwowanyMiod miod (5);
Obserwator *mis1 = new ObserwatorMis(&miod, 1);
Obserwator *mis2 = new ObserwatorMis(&miod, 2);
miod.dodaj(mis1);
miod.dodaj(mis2);
std::cout << "Ilosc miodu: " << miod.pobierzIlosc() << std::endl;
miod.ustawStan("Sa ludzie blisko miodu");
miod.ustawStan("Nie ma ludzi blisko miodu");
miod.ustawStan("Ida ludzie");
std::cout << "Ilosc miodu: " << miod.pobierzIlosc() << std::endl;
delete mis1;
delete mis2;
return 0;
}