Narzędzia użytkownika

Narzędzia witryny


obserwator

Wzorzec Projektowy Obserwator

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.

Diagram UML

Opis elementów wzorca

W skład wzorca wchodzą cztery elementy:

  1. Abstrakcyjny Obserwowany: Klasa abstrakcyjna dostarczająca interfejs dla dodawania i usuwania obserwatorów, oraz powiadamiania o zmianie stanu wszystkich obserwatorów z listy. Zawiera:
    • dodaj- dodaje obserwatora
    • usun- usuwa obserwatora
    • powiadom- powiadamia o zmianie wszystkich obserwatorow z listy poprzez wołanie metody odswież dla każdego z nich
  2. Obserwowany Konkretny: Klasa zwraca swój wewnętrzny stan, który jest obiektem zainteresowania obserwatorów. Powiadamia także swoich obserwatorów na wypadek zmiany swojego stanu. Zawiera:
    • podaj_stan - zwraca aktualny stan obiektu
  3. Abstrakcyjny Obserwator: Klasa abstrakcyjna dostarczająca interfejs dla powiadamiania dla wszystkich obserwatorów. Zawiera:
    • odswiez - abstrakcyjna metoda która ma być nadpisana przez konkretnego obserwatora
  4. Obserwator konkretny: Klasa pozostaję w związku z obiektem obserwowanym (ObserwowanyKonkretny) aby otrzymać/pobrać jego stan w wypadku powiadomienia o zmienie. Zawiera:
    • odswiez - podczas wołania tej metody (która nadpisuje metode abstrakcyjną w klasie bazowej), konkretny obserwator woła podaj_stan dla obiektu obserwowanego aby zaktualizować informację o nim

Kod w C++

Abstrakcyjny Obserwowany

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_;
};

Obserwowany konkretny

class ObserwowanyKonkretny : public Obserwowany {
	public:
	/**
	 * podaje stan obiektu obserwowanego
	 */
	StanObserwowanego podaj_stan() {
		//zwroc stan_
	}
	private:
	/**
	 * opisuje stan obiektu obserwowanego
	 */
	StanObserwowanego* stan_;
};

Abstrakcyjny Obserwator

class Obserwator {
	public:
	/**
	 * powiadamia obserwatora o zajsciu zmiany stanu w obiekcie obserwowanym
	 */
	virtual void odswiez() = 0;
	/**
	 * abstrakcyjny destruktor
	 */
	virtual ~Obserwator() {
		;
	}
};

Obserwator konkretny

class ObserwatorKonkretny : public Obserwator {
	public:
	/**
	 * powiadamia obserwatora o zajsciu zmiany stanu w obiekcie obserwowanym
	 */
	void odswiez() {
		/* uzyskaj aktualny stan obiektu obserwowanego */
	}
};

Typowe użycie

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:

  • do zależności dochodzi nowy obiekt
  • jeden z istniejących obiektów musi zostać zamieniony na inny (np. innej klasy)
  • musimy usunąć jeden z obiektów

Wygodnym sposobem jest utrzymywanie niezależnych obiektów współpracujących ze sobą bez wiedzy o swoim istnieniu - tak działa Wzorzec Obserwatora.

Działanie

Gdy zmienia się stan obiektu obserwowanego, odpowiednio powinien zmienić się stan obserwatorów. Istnieją dwa mechanizmy propagacji zmian stanu obiektu obserwowanego: Powiadamianie oraz Polling.

Powiadamianie

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:

  • aby obiekt obserwowany pozostał spójny po powiadomieniu obserwatorów
    istotne jest, aby zapewnić stabilność stanu obiektu obserwowanego przed i po powiadomieniu obserwatorów tak, aby każdy obserwator otrzymał tę samą informację o stanie obiektu obserwowanego
  • aby uniknąć redundantnych powiadomień podczas całego procesu zmiany stanu
    powiadamianie obserwatorów o każdej zmianie stanu obiektu obserwowanego nieraz jest niepotrzebne, zamiast tego lepszym wyjściem jest wysłanie jednego powiadomienia po całej grupie zmian jakie nastąpiły

Mechanizm ten jest prosty i oczywisty w wielu zastosowaniach, co czyni go najpowszechniejszym.

Polling

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).

Interakcje

Istnieją dwa modele interakcji pomiędzy obiektem obserwowanym a obserwatorami:

  • Push Model
    W tym modelu, obiekt obserwowany podaje zmiany swojego stanu obserwatorom. Model ten może zostać użyty gdy wszyscy obserwatorzy polegają na tych samych zmianach, obserwator po prostu odbiera wszystkie dane jakie dostaje od obiektu obserwowanego. Model ten nie jest wskazany podczas przekazywania dużej ilości danych podczas gdy każdy obserwator potrzebuje innej małej porcji danych.
  • Pull Model
    W tym modelu obserwatorzy sami pobierają potrzebne dane od obiektu obserwowanego. Model ten jest bardziej elastyczny, ponieważ obserwatorzy mogą pobrać tylko potrzebne dane.

Podsumowanie

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.

obserwator.txt · ostatnio zmienione: 2008/04/14 14:05 przez wojtek-j