
Wzorzec projektowy Obserwator
2025/04/05
Wprowadzenie
Wzorce projektowe to rozwiązania popularnych problemów programistycznych. Wyróżniamy następujące grupy wzorców:
- wzorce kreacyjne, które zajmują się tworzeniem obiektów, np. Fabryka, Budowniczy, Singleton.
- wzorce strukturalne, które pomagają grupować obiekty w większe struktury, np. Adapter, Dekorator.
- wzorce behawioralne, które wspomagają definiowanie ścieżek komunikacyjnych między obiektami, np. Obserwator, Strategia.
Obserwator (Observer) jest behawioralnym wzorcem projektowym. Jego głównym zadaniem jest przekazywanie informacji o zmianie stanu obiektu innym zainteresowanym obiektom. Stosuje się go głównie w sytuacjach, w której mamy do czynienia z relacją jeden do wielu, gdzie wiele obiektów musi być informowana o zmianie stanu obiektu głównego.
Problem i rozwiązanie
Budujemy aplikację prognozy pogody, która ma dostarczać najnowsze dane do różnych mediów. Każda z platform chce na bieżąco otrzymywać informacje o zmianach pogody.
Bez zastosowania wzorca projektowego aplikacja musiałaby na sztywno powiązać klasę prognozy pogody z każdą klasą reprezentującą media. Każde usunięcie lub dodanie nowej platformy ingerowałoby w istniejący kod klasy prognozy pogody.
Wzorzec Obserwator umożliwia informowanie dowolnej liczby mediów (obserwatorów) bez wiedzy na temat ich klas. W dowolnym momencie można bezinwazyjnie dodać nową platformę lub usunąć istniejącą.
Schemat działania
Poniższy diagram UML dzieli się na dwie zasadnicze części:
- lewa część odpowiedzialna za obiekt obserwowany (podmiot) – obiekt klasy WeatherForecast implementujący interfejs ObservableInterface.
- prawa część odpowiedzialna za obserwatorów obiektu – obiekty klas InternetNews, TvNews i RadioNews, implementujące interface ObserverInterface.

Implementacja
Struktura folderów i plików projektu
- observer
- alert
- InternetNews.php
- ObserverInterface.php
- RadioNews.php
- TvNews.php
- weather
- ObservableInterface.php
- WeatherForecast.php
- index.php
- alert
Kod źródłowy
Plik ObserverInterface.php
<?php
declare(strict_types=1);
namespace alert;
use weather\WeatherForecast;
interface ObserverInterface
{
public function updateForecast(WeatherForecast $weaterForecast): void;
}
Plik InternetNews.php
<?php
declare(strict_types=1);
namespace alert;
use alert\ObserverInterface;
use weather\WeatherForecast;
class InternetNews implements ObserverInterface
{
public function updateForecast(WeatherForecast $weaterForecast): void
{
echo "Internet - nowa prognoza pogody: ";
echo "temperatura: " . $weaterForecast->getTemperature() . " stopni, ";
echo "ciśnienie: " . $weaterForecast->getPressure() . " hPa <br />";
}
}
Plik RadioNews.php
<?php
declare(strict_types=1);
namespace alert;
use alert\ObserverInterface;
use weather\WeatherForecast;
class RadioNews implements ObserverInterface
{
public function updateForecast(WeatherForecast $weaterForecast): void
{
echo "Radio - nowa prognoza pogody: ";
echo "temperatura: " . $weaterForecast->getTemperature() . " stopni, ";
echo "ciśnienie: " . $weaterForecast->getPressure() . " hPa <br />";
}
}
Plik TvNews.php
<?php
declare(strict_types=1);
namespace alert;
use alert\ObserverInterface;
use weather\WeatherForecast;
class TvNews implements ObserverInterface
{
public function updateForecast(WeatherForecast $weaterForecast): void
{
echo "Telewizja - nowa prognoza pogody: ";
echo "temperatura: " . $weaterForecast->getTemperature() . " stopni, ";
echo "ciśnienie: " . $weaterForecast->getPressure() . " hPa <br />";
}
}
Plik ObservableInterface.php
<?php
declare(strict_types=1);
namespace weather;
use alert\ObserverInterface;
interface ObservableInterface
{
public function registerObserver(ObserverInterface $observerInterface): void;
public function unregisterObserver(ObserverInterface $observerInterface): void;
public function notifyObservers(): void;
}
Plik WeatherForecast.php
<?php
declare(strict_types=1);
namespace weather;
use weather\ObservableInterface;
use alert\ObserverInterface;
class WeatherForecast implements ObservableInterface
{
private int $temparature;
private int $pressure;
private array $registeredObservers = [];
public function __construct(int $temparature, int $pressure)
{
$this->temparature = $temparature;
$this->pressure = $pressure;
}
public function setTemperature(int $temperature): void
{
$this->temparature = $temperature;
}
public function setPressure(int $pressure): void
{
$this->pressure = $pressure;
}
public function getTemperature(): int
{
return $this->temparature;
}
public function getPressure(): int
{
return $this->pressure;
}
public function registerObserver(ObserverInterface $observerInterface): void
{
array_push($this->registeredObservers, $observerInterface);
}
public function unregisterObserver(ObserverInterface $observerInterface): void
{
foreach ($this->registeredObservers as $key => $observer) {
if ($observer === $observerInterface) {
unset($this->registeredObservers[$key]);
}
}
}
public function notifyObservers(): void
{
foreach ($this->registeredObservers as $observer) {
$observer->updateForecast($this);
}
}
public function updateForecast(int $temperature, int $pressure)
{
$this->setTemperature($temperature);
$this->setPressure($pressure);
$this->notifyObservers();
}
}
Plik index.php
<?php
declare(strict_types=1);
require_once __DIR__ . "/alert/ObserverInterface.php";
require_once __DIR__ . "/alert/InternetNews.php";
require_once __DIR__ . "/alert/RadioNews.php";
require_once __DIR__ . "/alert/TvNews.php";
require_once __DIR__ . "/weather/ObservableInterface.php";
require_once __DIR__ . "/weather/WeatherForecast.php";
use alert\{InternetNews, RadioNews, TvNews};
use weather\WeatherForecast;
$weaterForecast = new WeatherForecast(22, 1013);
$internetNews = new InternetNews();
$radioNews = new RadioNews();
$tvNews = new TvNews();
$weaterForecast->registerObserver($internetNews);
$weaterForecast->registerObserver($radioNews);
$weaterForecast->registerObserver($tvNews);
$weaterForecast->notifyObservers();
$weaterForecast->unregisterObserver($radioNews);
$weaterForecast->unregisterObserver($tvNews);
echo "-----------------------------------------------------------------------------" . "<br />";
echo "Nowa prognoza - powiadomienie tylko dla serwisu internetowego:" . "<br />";
$weaterForecast->updateForecast(25, 1000);
Wynik działania:
Internet - nowa prognoza pogody: temperatura: 22 stopni, ciśnienie: 1013 hPa
Radio - nowa prognoza pogody: temperatura: 22 stopni, ciśnienie: 1013 hPa
Telewizja - nowa prognoza pogody: temperatura: 22 stopni, ciśnienie: 1013 hPa
-----------------------------------------------------------------------------
Nowa prognoza - powiadomienie tylko dla serwisu internetowego:
Internet - nowa prognoza pogody: temperatura: 25 stopni, ciśnienie: 1000 hPa
Podsumowanie
Na powyższym przykładzie najpierw dodano obserwatorów do podmiotu, obserwatorzy zostali powiadomieni o prognozie pogody, a następnie pozostawiono tylko jednego obserwatora, który otrzymał prognozę po raz drugi. Klient ma możliwość dodawania i usuwania obiektów obserwujących podmiot, dlatego liczba obserwujących nie musi być znana na samym początku. Co więcej można dodawać do aplikacji nowe klasy implementujące interfejs ObserverInterface bez konieczności modyfikacji istniejącego kodu – spełniamy wtedy Zasadę otwarte-zamknięte (2 zasada SOLID).