Wzorce projektowe: Stan(State)

Dzisiaj o dosyć prostym wzorcu zwanym Stan(State), który służy do bardzo prostej czynności, jak sama nazwa mówi zmianie zachowania obiektu, kiedy zmieni się jego wewnętrzny stan, czyli np kiedy stanie się jakieś zdarzenie, dokładniej już tłumaczę dalej w artykule 🙂

 

Dyskusja

Najprościej mówiąc we wzorcu stan chodzi o to, że jeśli wewnętrzny stan obiektu się zmieni(możemy bazować na jednej ze zmiennych tego obiektu np na typie bool) z prawdy na fałsz to wtedy zmieniamy zachowanie tego obiektu.

Najważniejsze elementy w implementacji tego wzorca to:

  • Klasa Context, która przechowuje obecny stan jakiegoś obiektu, nadpisuje ten stan oraz zmienia go.
  • Abstrakcyjna klasa State, która posiada zdefiniowane abstrakcyjne metody potrzebne do zmiany stanu obiektu.
  • Klasa ConcreteState, która dziedziczy po klasie State implementując jej metody pozwalajace zmieniać stan obiektu.

Wzorca State używa się wszędzie gdzie są jakieś zdarzenia, czy warunki do spełnienia czasem lepiej jest użyć wzorca State kiedy mamy dużo skomplikowanych instrukcji warunkowych wtedy lepiej je zastąpić wzorcem State. Czyli w skrócie wzorca State możemy użyć wszędzie tam gdzie jest skomplikowana logika warunkowa. Np piszemy artykuł tak jak w wordpressie i mamy stany artykułu Draft, Published i tak dalej, tutaj można użyć wzorca State.

 

Cel

  • Zmiana zachowania obiektu kiedy zmieni się jego wewnętrzny stan.
  • Zminimalizowanie złożoności instrukcji warunkowych.

 

Problem

Musisz zmieniać zachowanie obiektu podczas czasu życia programu tzw runtime lub możesz mieć zestaw dosyć skomplikowanych instrukcji warunkowych wtedy lepszym rozwiązaniem będzie użycie wzorca State, niż kilkudziesięciu instrukcji warunkowch.

 

Użyj wtedy kiedy:

  • W dużych projektach, musisz zmieniać zachowanie obiektu w zależności od jego stanu(W małych oraz średnich projektach używanie wzorca State nie ma sensu dodaje jedynie niepotrzebną skomplikowaną logikę).
  • Masz zestaw skomplikowanych instrukcji warunkowych.

 

Struktura

Standardowo diagram UML wzorca State:

Tu już wiadomo co robią te klasy. StateOne, StateTwo i StateThree to oczywiście klasy ConcreteState, dziedziczące po klasie abstrakcyjnej State.

Warto tylko wspomnieć, że klient operuje klasami konkretnymi tylko z poziomu klasy Context.

 

Przykłady

Schemat wzorca State w kodzie

I wiadomo już, że w tym kawałku artykułu przechodzimy do prawdziwej roboty 🙂

Pokaże w tym przykładzie o co chodzi we wzorcu State. Zacznijmy od klasy Context.

Do tej klasy nadpisujemy stan obiektu, przechowujemy go oraz zmieniamy go metodą Request() przekazując tą metodą klasę Context do klasy konkretnej dziedziczącej po klasie abstrakcyjnej State.

Zobaczmy jak wygląda klasa State oraz klasy konkretne po niej dziedziczące.

Najpierw klasa State.

W tej klasie nie ma nic do rozumienia, mamy po prostu zdefiniowaną metodę goNext(), żeby zaimplementować ją w klasach konkretnych StateOne, StateTwo oraz StateThree dziedziczących po klasie State. Oraz po to, żeby w klasie Context nie używać klasy typu konkretnego.

Zobaczmy klasy konkretne dziedziczące po klasie State.

Najpierw klasa StateOne.

StateTwo

StateThree

Widzimy, że w każdej tej klasie z każdym wywołaniem metody goNext(), zmieniamy stan obiektu w klasie Context na inny.

I oczywiście klient.

W kliencie tylko tworzymy obiekt klasy Context oraz ustawiamy początkowy stan obiektu dalej tylko zmieniamy stan tego obiektu metodą Request().

Wynik:

 

Stany dzwonka

Zróbmy jeszcze jeden prosty przykład z np ze stanami dzwonka.

Zacznijmy od klasy Context, czyli w tym przypadku Bell.

Teraz klasa State w tym przypadku klasa BellState.

I klasy konkretne dziedziczące po klasie BellState, czyli najpierw klasa BellRingingState.

I klasa BellSilentState.

I na końcu klient.

Wynik:

 

Przykład z życia wzięty

Automat

Zróbmy teraz przykład automatu. Załóżmy, że chcesz zrobić biznes stawiając automaty przy drodze, które mogą sprzedawać produkty spożywcze i potrzebujesz programu, który będzie zmieniał stany tej maszyny, np gdy wpłacimy gotówkę to wtedy zmieniamy stan maszyny ze stanu oczekującego na gotówkę na stan wydania produktu.

Trudno pokazać zalety wzorca State w tak prostych przykładach, w takich przykładach wzorzec State dodaje jedynie niepotrzebnie więcej kodu, używa się go w większych projektach, ale nie będe robił jakiegoś długiego przykładu wtedy ciężko będzie go zrozumieć, ale poniższy przykład powinien dać do zrozumienia jak bardzo wzorzec State przydaje się w dużych projektach.

Zrobimy w kolejności tak jak w poprzednim przykładzie.

Zacznijmy od klasy Context w tym przypadku będzie się nazywała VendingMachine.

Klasa VendingMachineState

Oraz klasy konkretne dziedziczące po klasie VendingMachineState, czyli najpierw klasa VendingDepositeState.

W tej klasie mamy warunki, że jeśli klient wpłaci za dużo to wypłacamy resztę klientowi oraz zatwierdzamy wpłatę, jeśli za mało, wyświetlamy komuniakt.

I klasa VendingStockState, za pomocą której dajemy klientowi produkt za który zapłacił.

I na końcu klient.

Wynik:

 

Powiązania z innymi wzorcami

  • Obiekty wzorca State są często singletonami.
  • Wzorzec Pyłek(Flyweight) może użyć wzorca State do dzielenia obiektów.
  • Interpreter może użyć wzorca State do zdefiniowania klasy Context, która parsuje wszystkie dane.

Wzorzec State jest również podobny do wzorca Strategia(Strategy) o którym będzie mowa w kolejnym artykule 🙂

 

Podsumowanie

I to wszystko na temat wzorca Stan(State)🙂

Link do githuba ze wszystkimi przykładami: https://github.com/Slaw145/State

W następnym artykule, będzie mowa o wzorcu Strategia(Strategy).

Standardowo, przypominam o newsletterze, którym wysyłam powiadomienia o nowych wpisach oraz dodatkowe informacje na temat, ogółem mówiąc, świecie IT.

KONIECZNIE dołącz do społeczności DevmanCommunity na fb, części społeczności jest w jednym miejscu 🙂

-strona na fb: Devman.pl-Sławomir Kowalski

-grupa na fb: DevmanCommunity

Pytaj, komentuj pod spodem na końcu wpisu, podziel się nim, oceń go, rób co wolisz🙂

Ilustracje, obrazki oraz diagramy są z: https://sourcemaking.com/design_patterns/state

 
Jeśli ten wpis ci się przydał podziel się nim ze swoimi znajomymi :)

Post a comment

Be first!

avatar
  Subscribe  
Notify about