Wzorzec Observer
Część sekcji Programowanie obiektowe ścieżki PHP w Coddy. Lekcja 73 z 91.
Wzorzec Obserwator to behawioralny wzorzec projektowy, który ustanawia relację jeden-do-wielu między obiektami. Gdy jeden obiekt (podmiot) zmienia stan, wszystkie obiekty od niego zależne (obserwatorzy) są automatycznie powiadamiane i aktualizowane.
Pomyśl o subskrypcji newslettera. Newsletter jest podmiotem, a subskrybenci są obserwatorami. Gdy ukazuje się nowe wydanie, wszyscy subskrybenci otrzymują je automatycznie, bez konieczności poznawania przez newsletter szczegółów dotyczących każdego subskrybenta.
Oto podstawowa implementacja w PHP:
<?php
interface Observer {
public function update(string $message): void;
}
class Newsletter {
private array $subscribers = [];
public function subscribe(Observer $observer): void {
$this->subscribers[] = $observer;
}
public function publish(string $content): void {
foreach ($this->subscribers as $subscriber) {
$subscriber->update($content);
}
}
}
class EmailSubscriber implements Observer {
public function __construct(private string $email) {}
public function update(string $message): void {
echo "Sending to {$this->email}: $message\n";
}
}
$newsletter = new Newsletter();
$newsletter->subscribe(new EmailSubscriber("alice@example.com"));
$newsletter->subscribe(new EmailSubscriber("bob@example.com"));
$newsletter->publish("New article released!");
Wynik:
Sending to alice@example.com: New article released!
Sending to bob@example.com: New article released!Ten wzorzec oddziela podmiot od jego obserwatorów. Klasa Newsletter nie musi wiedzieć, czy powiadamia subskrybentów e-maili, subskrybentów SMS-ów czy usługi powiadomień push. Po prostu wywołuje update() na wszystkim, co implementuje interfejs Observer. Dzięki temu dodawanie nowych typów obserwatorów jest łatwe i nie wymaga modyfikowania istniejącego kodu.
Wyzwanie
ŁatwyZbudujmy system alertów o cenach akcji z wykorzystaniem wzorca Obserwator. Gdy cena akcji się zmieni, wszyscy zarejestrowani inwestorzy powinni automatycznie otrzymać powiadomienie — to doskonałe zastosowanie tego wzorca w prawdziwym świecie.
Rozmieścisz kod w czterech plikach:
Observer.php: Zdefiniuj interfejsObserverz jedną metodąupdate(string $stockName, float $price): void. To kontrakt, którego muszą przestrzegać wszyscy obserwatorzy, aby otrzymywać powiadomienia.Investors.php: Dołącz plik Observer i utwórz dwie klasy implementujące ten interfejs:EmailInvestor: Przyjmuje adres e-mail w konstruktorze (użyj promocji właściwości konstruktora). Jego metodaupdate()wyświetla"Email to [email]: [stockName] is now $[price]"SmsInvestor: Przyjmuje numer telefonu w konstruktorze. Jego metodaupdate()wyświetla"SMS to [phone]: [stockName] is now $[price]"
Stock.php: Dołącz plik Observer i utwórz klasęStock, która pełni rolę podmiotu. Twoja akcja powinna:- Przechowywać nazwę akcji i jej bieżącą cenę (użyj promocji właściwości konstruktora dla nazwy, zainicjuj cenę wartością
0.0) - Utrzymywać prywatną tablicę obserwatorów
- Mieć metodę
subscribe(Observer $observer): voiddo dodawania obserwatorów - Mieć metodę
setPrice(float $price): void, która aktualizuje cenę i powiadamia wszystkich obserwatorów
update()każdego obserwatora, przekazując nazwę akcji i nową cenę.- Przechowywać nazwę akcji i jej bieżącą cenę (użyj promocji właściwości konstruktora dla nazwy, zainicjuj cenę wartością
main.php: Dołącz pliki Investors i Stock. Otrzymasz trzy dane wejściowe: nazwę akcji, adres e-mail i numer telefonu.Utwórz obiekt
Stockz podaną nazwą. ZasubskrybujEmailInvestorz adresem e-mail orazSmsInvestorz numerem telefonu.Ustaw cenę akcji na
150.50, aby wywołać powiadomienia dla wszystkich subskrybentów.
Wzorzec Obserwator pozwala oddzielić klasę Stock od konkretnych metod powiadamiania. Możesz później łatwo dodać PushInvestor lub SlackInvestor, nie modyfikując przy tym klasy Stock.
Spróbuj swoich sił
<?php
require_once 'Investors.php';
require_once 'Stock.php';
// Wczytaj dane wejściowe
$stockName = trim(fgets(STDIN));
$email = trim(fgets(STDIN));
$phone = trim(fgets(STDIN));
// TODO: Utwórz Stock o podanej nazwie
// TODO: Zapisz EmailInvestor, używając adresu e-mail
// TODO: Zapisz SmsInvestor, używając numeru telefonu
// TODO: Ustaw cenę akcji na 150.50, aby wywołać powiadomienia
?>Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektySłowo kluczowe $thisMetodyWłaściwościKonstruktor (__construct)Destruktor (__destruct)Powtórka — prosty kalkulator4Dziedziczenie
Podstawy dziedziczeniaSłowo kluczowe parent::Przesłanianie metodSłowo kluczowe finalKlasy abstrakcyjnePowtórzenie – hierarchia pracowników7Enkapsulacja
Public, protected, privateModyfikatory dostępu w szczegółachGettery i setteryUkrywanie informacjiPromocja parametrów konstruktora (8.0)Podsumowanie — system ewidencji studentów2Przestrzenie nazw i autoloading
Wprowadzenie do przestrzeni nazwSłowo kluczowe useStandard autoloadingu PSR-4Autoloader ComposeraPodsumowanie – uporządkowany projekt5Interfejsy i kontrakty
Wprowadzenie do interfejsówImplementowanie interfejsówImplementowanie wielu interfejsówInterfejs a klasa abstrakcyjnaPodpowiadanie typów za pomocą interfejsówPodsumowanie — kalkulator kształtów8Metody magiczne
Wprowadzenie do metod magicznych__toString i __debugInfo__get, __set, __isset, __unset__call i __callStatic__clone i klonowanie obiektów__serialize i __unserializePodsumowanie — własna kolekcja11System typów i obsługa błędów
Deklaracje typówTypy nullableTypy sumy i przecięciaKlasy wyjątkówNiestandardowa hierarchia wyjątkówTry, Catch, FinallyPodsumowanie — walidator formularzy14Projekt: Zarządzanie biblioteką
Przegląd projektuKlasy książki i użytkownika3Właściwości klas
Właściwości instancji a właściwości statyczneStałe w klasachMetody i właściwości statyczneWłaściwości prywatne i chronioneWłaściwości tylko do odczytu (PHP 8.1)Podsumowanie — menedżer kont bankowych6Polimorfizm
Ponowne omówienie nadpisywania metodPolimorfizm za pomocą interfejsówPodpowiedzi typów i typy sumyPóźne wiązanie statycznePodsumowanie — procesor płatności9Cechy
Wprowadzenie do cechKorzystanie z wielu cechRozwiązywanie konfliktów cechMetody abstrakcyjne w cechachCechy a dziedziczenie12Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec StrategyPoćwicz samodzielnie: Kompilator PHP online