Wzorzec Obserwator (ang. Observer) używany jest do powiadamiania wybranych obiektów o zmianie stanu innych obiektów. Dzięki zastosowaniu tego wzorca można odizolować od siebie obiekty tak, że nie są one ze sobą sztywno powiązane. Zgodnie z poniższym diagramem we wzorcu występują dwa podstawowe typy obiektów (interfejsy):
- obserwowany – obiekt, który jest śledzony; kiedy zmieni się jego stan – wywołuje metodę powiadomObserwatorow() i informuje o tym wszystkich obserwatorów. Obiekt obserwowany dodaje i usuwa obserwatorów korzystając z dwóch metod: dodajObserwatora(), usunObserwatora(),
- obserwator – obiekt oczekujący na powiadomienie o zmianie stanu obiektu obserwowanego. Obserwator otrzymuje referencję do obiektu obserwowanego.

Aby uprościć sobie życie skorzystamy z wbudowanego w PHP interfejsu obserwatora SplObserver.
Przykład implementacji
W poniższym przykładzie mamy dwóch obserwatorów: Observer1 i Observer2, którzy wyświetlą krótki tekst reagując na powiadomienie – notify() z podmiotu obserwowanego Subject.
1 2 3 4 5 6 7 8 9 |
<?php // Observer1.php class Observer1 implements SplObserver { public function update(SplSubject $subject) { echo '<p>Test 1</p>'; } } |
1 2 3 4 5 6 7 8 9 |
<?php // ConcreteObserver2.php class Observer2 implements SplObserver { public function update(SplSubject $subject) { echo '<p>Test 2</p>'; } } |
Klasa Subject, czyli podmiot obserwowany ustawia w konstruktorze uchwyt do wbudowanej w PHP biblioteki SPL. Korzystając z tej biblioteki mamy do dyspozycji gotowe metody: attach (dodaje obserwatora/ów), detach (usuwa obserwatora/ów) oraz notify (powiadamia obserwatora/ów o zmianie stanu obiektu/ów).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<?php // Subject.php class Subject implements SplSubject { private $_observers; public function __construct() { $this->_observers = new SplObjectStorage(); } public function attach(SplObserver $observer) { $this->_observers->attach($observer); } public function detach(SplObserver $observer) { $this->_observers->detach($observer); } public function notify() { foreach ($this->_observers as $observer) { $observer->update($this); } } } |
Klasa testująca:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<?php // Client.php spl_autoload_register(function ($class_name) { include $class_name . '.php'; }); // obiekty obserwujące (obserwatorzy) $observer1 = new Observer1(); $observer2 = new Observer2(); // obiekt obserwowany $subject = new Subject(); // dodajemy obiekty obserwujące $subject->attach($observer1); $subject->attach($observer2); // wysyłamy powiadomienia do wszystkich obserwatorów $subject->notify(); // usuwamy wybranego obserwatora $subject->detach($observer2); // ponownie wysyłamy powiadomienia $subject->notify(); // ... /* Wyświetli: Test 1 Test 2 Test 1 */ |
Konkretny przykład
Obiekt obserwowany Mailing posiada metodę add(), która symuluje dodanie adresu e-mail do bazy danych subskrybentów. Jeśli wszystko odbędzie się bez komplikacji a funkcja zwróci $this->state = true, uruchomione zostanie zdarzenie notify() z powiadomieniem, które odbierze obserwator – SendMail. Obserwator wysyła wiadomość mailową potwierdzającą zapis do bazy mailingowej.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php // SendMail.php class SendMail implements SplObserver { public function update(SplSubject $mailing) { if ($mailing->state == true) { mail( 'to@example.com', 'Subscriber', 'Your email address has been added to the mailing list.' ); echo 'Success!'; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?php // Mailing.php class Mailing implements SplSubject { private $_observers; public $state; public function __construct() { $this->_observers = new SplObjectStorage(); } public function attach(SplObserver $observer) { $this->_observers->attach($observer); } public function detach(SplObserver $observer) { $this->_observers->detach($observer); } public function notify() { foreach ($this->_observers as $observer) { $observer->update($this); } } public function add() { // example: add record to the database $this->state = true; // success // and: $this->notify(); } } |
1 2 3 4 5 6 7 8 9 |
<?php // Client.php spl_autoload_register(function ($class_name) { include $class_name . '.php'; }); $observer = new SendMail(); $mailing = new Mailing(); $mailing->attach($observer); $mailing->add(); |