Wzorzec Obserwatora (ang. The Observer Pattern) jest wzorcem projektowym służącym do obserwowania stanu obiektu. Wzorzec ten jest pomyślany do rozwiązania sytuacji w której stan większej liczby obiektów (obserwatorów) zależy od stanu innego obiektu (obserwowanego) - pomaga zachować spójność między nimi. W ogólnym przypadku działanie jego polega na rejestrowaniu przez obiekt obserwowany obiektów go obserwujących (Obserwatorów), a następnie po zmianie swojego stanu powiadomieniu wszystkich Obserwatorów o tym fakcie tak, aby każdy z nich mógł odpowiednio zaktualizować swój stan. Wzorzec jest szczególnie przydatny w przypadkach gdy rodzaj i ilość obserwatorów nie jest znana lub zmienna.
W skład wzorca wchodzą cztery elementy:
class Obserwowany { public: /** * dodaje obserwatora o do listy obserwatorow */ void dodaj(Obserwator* o) { //dodaj obserwatora o do listy obserwatorow } /** * powiadamia kazdego obserwatora o zmianie stanu */ void powiadom(){ //dla każdego o w obserwatorzy wywołaj o->odswiez() } /** * abstrakcyjny destruktor */ virtual ~Obserwowany() { ; }; private: /** * lista obserwatorow */ std::vector<Obserwator*> obserwatorzy_; };
class ObserwowanyKonkretny : public Obserwowany { public: /** * podaje stan obiektu obserwowanego */ StanObserwowanego podaj_stan() { //zwroc stan_ } private: /** * opisuje stan obiektu obserwowanego */ StanObserwowanego* stan_; };
class Obserwator { public: /** * powiadamia obserwatora o zajsciu zmiany stanu w obiekcie obserwowanym */ virtual void odswiez() = 0; /** * abstrakcyjny destruktor */ virtual ~Obserwator() { ; } };
class ObserwatorKonkretny : public Obserwator { public: /** * powiadamia obserwatora o zajsciu zmiany stanu w obiekcie obserwowanym */ void odswiez() { /* uzyskaj aktualny stan obiektu obserwowanego */ } };
Dla przykładu weźmy dwa obiekty A oraz B, przy czym stan obiektu A jest zależny od stanu obiektu B, czyli może się zdarzyć że po zmianie stanu obiektu B obiekt A również powinien zmienić swój stan. W tym ogólnym (i prostym) przypadku można pokusić się o bezpośrednią interakcje pomiędzy obiektami poprzez utrzymanie w jednym z nich wskaźnika lub referencji na drugi. Sytuacja jednak komplikuje się kiedy:
Wygodnym sposobem jest utrzymywanie niezależnych obiektów współpracujących ze sobą bez wiedzy o swoim istnieniu - tak działa Wzorzec Obserwatora.
Gdy zmienia się stan obiektu obserwowanego, odpowiednio powinien zmienić się stan obserwatorów. Istnieją dwa mechanizmy propagacji zmian stanu obiektu obserwowanego: Powiadamianie oraz Polling.
Po zmianie swojego stanu obiekt obserwowany powiadamia obserwatorów (metoda powiadom) o tym fakcie, po czym obserwatorzy pobierają informację o zmianach wywołując metodę podaj_stan na obiekcie obserwowanym. Podczas tej procedury należy zadbać o dwa istotne fakty:
Mechanizm ten jest prosty i oczywisty w wielu zastosowaniach, co czyni go najpowszechniejszym.
W tym mechanizmie, obiekt obserwowany nie powiadamia obserwatorów o zmianie swojego stanu, zamiast tego obserwatorzy odpytują obiekt obserwowany o jego stan. Plusem jest kompletna niezależność obiektu obserwowanego od obserwatorów, trzeba jednak zadbać o to aby odpytywać obiekt obserwowany w odpowiednim momencie - w szczególności nie za często aby uniknąć pustych akcji (żadne zmiany nie nastąpiły) ani nie za rzadko (zmiany mogły nastąpić kilkukrotnie).
Istnieją dwa modele interakcji pomiędzy obiektem obserwowanym a obserwatorami:
Wzorzec obserwatora pozwala uniknąć niepotrzebnych (nieraz uciążliwych albo niemożliwych) bezpośrednich interakcji pomiędzy obiektami. Przykładem takiej sytuacji jest problem w którym nie znamy ilości obiektów które pozostaną w zależności lub też ilość ta może ulegać zmianie. Przy wykorzystaniu tego wzorca dodawanie i usuwanie obserwatorów nie stanowi problemu.
W większości przypadków, obserwatorzy zostaną tylko powiadomieni o zmianach. Znaczy to, że sami będą musieli je rozpoznać i ocenić czy ich dotyczą, co jednak może zostać rozwiązane poprzez umieszczanie odpowiedniej informacji w powiadomieniu.
Obiekty obserwatorów są od siebie zupełnie niezależne, natomiast wszystkie polegają na stanie obiektu obserwowanego, toteż należy zwrócić uwagę na zmiany stanu obiektu obserwowanego jakie mogą zostać wywołane przez obserwatora, zwłaszcza podczas wykonywania przez niego metody odswiez. Sytuacja taka pociąga za sobą jeszcze jedno niebezpieczeństwo, otóż jeśli zmiana stanu obiektu obserwowanego następuję w metodzie odswiez, bardzo łatwo o kolejne powiadomienie a co za tym idzie kolejne wywołania metody odswiez - co w takim wypadku prowadzi do rekurencji.
Jako że nie istnieje jawna interakcja pomiędzy obiektem obserwowany a obserwatorami, łatwo doprowadzić do sytuacji w której obiekt obserwowany przestaje być tym czym był do tej pory (w szczególności zostaje usunięty) bez podania odpowiedniej informacji do obserwatorów.