Wzorzec stanu
Część sekcji Programowanie obiektowe ścieżki PHP w Coddy. Lekcja 79 z 91.
Wzorzec Stan to behawioralny wzorzec projektowy, który pozwala obiektowi zmieniać swoje zachowanie, gdy zmienia się jego stan wewnętrzny. Zamiast używać złożonych instrukcji warunkowych do obsługi różnych stanów, zamykasz każdy stan w osobnej klasie i delegujesz zachowanie do obiektu reprezentującego bieżący stan.
Pomyśl o automacie sprzedającym. Zachowuje się inaczej w zależności od tego, czy czeka na pieniądze, otrzymał płatność, czy wydaje produkt.
Każdy stan ma własne zasady określające, jakie działania są dozwolone. Wzorzec Stanu modeluje to, tworząc osobną klasę dla każdego stanu.
<?php
interface OrderState {
public function proceed(Order $order): string;
public function getStatus(): string;
}
class PendingState implements OrderState {
public function proceed(Order $order): string {
$order->setState(new ProcessingState());
return "Order moved to processing";
}
public function getStatus(): string {
return "Pending";
}
}
class ProcessingState implements OrderState {
public function proceed(Order $order): string {
$order->setState(new ShippedState());
return "Order has been shipped";
}
public function getStatus(): string {
return "Processing";
}
}
class ShippedState implements OrderState {
public function proceed(Order $order): string {
return "Order already shipped";
}
public function getStatus(): string {
return "Shipped";
}
}
Klasa kontekstu przechowuje odwołanie do bieżącego stanu i przekazuje mu wywołania:
<?php
class Order {
private OrderState $state;
public function __construct() {
$this->state = new PendingState();
}
public function setState(OrderState $state): void {
$this->state = $state;
}
public function proceed(): string {
return $this->state->proceed($this);
}
public function getStatus(): string {
return $this->state->getStatus();
}
}
$order = new Order();
echo $order->getStatus() . "\n";
echo $order->proceed() . "\n";
echo $order->getStatus() . "\n";
echo $order->proceed() . "\n";
echo $order->getStatus();
Wynik:
Pending
Order moved to processing
Processing
Order has been shipped
ShippedWzorzec Stan eliminuje rozbudowane instrukcje switch i ułatwia dodawanie nowych stanów. Każda klasa stanu odpowiada za znajomość kolejnego stanu, dzięki czemu logika jest uporządkowana i łatwa w utrzymaniu.
Wyzwanie
ŁatwyZbudujmy system obiegu dokumentów z użyciem wzorca State. Dokumenty w systemie zarządzania treścią przechodzą przez różne etapy: wersję roboczą, weryfikację i publikację. Każdy etap ma własne zasady dotyczące dozwolonych działań i tego, co dzieje się, gdy próbujesz przejść do kolejnego etapu.
Rozmieścisz swój kod w czterech plikach:
DocumentState.php: Zdefiniuj interfejsDocumentState, który określa kontrakt dla wszystkich stanów dokumentu. Powinien mieć dwie metody:publish(Document $document): stringdo próby przejścia dokumentu do kolejnego etapu orazgetStatus(): stringdo zwracania nazwy bieżącego stanu.States.php: Dołącz interfejs DocumentState i utwórz trzy klasy stanów, które go implementują:DraftState: Gdy wywołana zostanie metodapublish(), zmienia stan dokumentu naReviewStatei zwraca"Document sent for review". Jego status to"Draft".ReviewState: Gdy wywołana zostanie metodapublish(), zmienia stan dokumentu naPublishedStatei zwraca"Document published". Jego status to"Under Review".PublishedState: Gdy wywołana zostanie metodapublish(), zwraca"Document is already published"bez zmiany stanu. Jego status to"Published".
Document.php: Dołącz plik States i utwórz klasęDocument, która pełni rolę kontekstu. Twój dokument powinien:- Przyjmować tytuł w konstruktorze i rozpoczynać w stanie
DraftState - Mieć metodę
setState(DocumentState $state): voiddo zmiany bieżącego stanu - Mieć metodę
publish(): string, która deleguje wywołanie do metodypublish()bieżącego stanu - Mieć metodę
getStatus(): string, która zwraca status bieżącego stanu
- Przyjmować tytuł w konstruktorze i rozpoczynać w stanie
main.php: Dołącz plik Document. Otrzymasz jedno wejście: liczbę wywołań metodypublish()dla dokumentu.Utwórz obiekt
Documentz tytułem"My Article". Najpierw wypisz początkowy status. Następnie wywołaj metodępublish()podaną liczbę razy, wypisując wynik każdego wywołania w nowej linii. Na koniec wypisz końcowy status.Format wyjścia:
Status: [initial status] [publish result 1] [publish result 2] ... Status: [final status]
Każda klasa stanu wie, który stan następuje po niej w obiegu, dzięki czemu logika przejść jest uporządkowana i łatwa do modyfikacji. Dodanie nowego stanu, takiego jak "Archived", będzie po prostu polegało na utworzeniu nowej klasy i zaktualizowaniu odpowiednich przejść.
Spróbuj swoich sił
<?php
require_once 'Document.php';
// Wczytaj dane wejściowe — liczbę wywołań publish()
$n = intval(trim(fgets(STDIN)));
// TODO: Utwórz Document o tytule "My Article"
// TODO: Wypisz początkowy status w formacie: "Status: [status]"
// TODO: Wywołaj publish() n razy, wypisując każdy wynik w nowym wierszu
// TODO: Wypisz końcowy status w formacie: "Status: [status]"
?>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ów10Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieWstrzykiwanie zależnościKlasy anonimoweEnumy (PHP 8.1)Włókna (PHP 8.1)Dogłębne omówienie klonowania obiektówGeneratory i iteratory13Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytuWzorzec repozytorium2Przestrzenie 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