Wzorce projektowe – Wzorzec projektowy Stan (State Design Pattern)

category

c#

author

admin

Przy okazji przeglądania przykładu z Zenject natrafiłem na bardzo fajny przykład wykorzystania wzorca projektowego stan w przypadku klasy Ship.

Zobaczmy co ma a czego nie ma klasa Ship:
– ma fabrykę służącą do tworzenia nowych stanów. Fabryka stanów jest wstrzykiwana w funkcji [Inject] (Fabryka nie ma referencji do statku)
– ma funkcję do zmiany stanów, która przy okazji wywołuje funkcję state.Start();

Klasa Ship nie ma:
– żadnej logiki poza zmianą stanów
– funkcje z Unity wywołują funkcje z konkretych stanów, np. funckja Update klasy Ship tak naprawdę wykonuje Update() z aktualnego stanu:

Wszystkie stany mają jedną wspólną klasę, której funkcje można opcjonalnie nadpisać (Update trzeba, ale to też można zmienić jak trzeba).
(IDisposable też jest naturalnie opcjonalne)

Warto tutaj zauważyć, że funkcja w stanie może przyjmować parametry.

A jakie mamy stany?

ShipStateWaitingToStart
ShipStateMoving
ShipStateDead

No ok, ale jak operować np. na pozycji statku? W ogóle jak stan ma operować na klasie Ship?

Każdy stan dostaje instancję Ship w konstuktorze. Co ciekawe, różne stany mogą mieć różne parametry w konstruktorach. Jako że używamy Zenject, to za wszystkie parametry odpowiadają bindingi i [Inject]. Czyli stan dostaje statek, ale nie obchodzi go skąd on się bierze.

Statek natomiast nie ma pojęcia o konkretnych stanach. Zna tylko klasę bazową ShipState. Konkretne stany zwraca fabryka. Ship nie ma pojęcia co to jest za stan i jaka jest w nim logika.

No a jak te stany się zmieniają? Co i kiedy je zmienia?

Ship ma funkcję ChangeState, która jako parametr przyjmuje enum jaki Ship ma mieć nowy stan. Funkcja potem przekazuje tego enuma do fabryki a fabryka wypluwa z siebie nowy stan które jest przypisywany do zmiennej _state statku.

A co może wywoływać funkcję ChangeState?

A no statek może sam sobie zmienić stan:

GameController może zmieniać stan, np. kiedy zaczyna się gra:

Albo inny stan może zmienić stan statku na inny, np. StateStateMoving może zmienić stan na ShipStateDead w razie kolizji:

A jak działają te fabryki stanów?

Każdy klasa stanu ma swoją “Zenjectową” fabryk. Np. :

Następnie te wszystkie fabryki są zebrane w jednej “zwykłej” klasie która ma metodę służącą do tworzenia stanów. Aż wkleję cały kod żeby było jasne o co chodzi:

No to tutaj chyba z użyciem Zenject jest wszystko jasne. Ale jeśli nie mamy jakiegoś “magicznego” sposobu wstrzykiwania Statku? Rzućmy okiem na przykład z książki “Head First: Design Patterns”:

Tam jest przykład z maszyną do gum i jej stanami.

Pierwsza opcja:
Każdy stan maszyny jako parametr dostaje tą maszynę (this) bo maszyna sama sobie tworzy te stany.
Tam chyba był przykład z fabryką stanów przy tej maszynie.

Leave a comment