Kazachstan to też rodzaj stanu (źródło: film Borat)
Lata 80-te ubiegłego wieku, Związek Radziecki. Na komisji poborowej, przed doświadczonym sierżantem stoi osiemnastoletni poborowy. Sierżant uśmiecha się szeroko i mówi: „No młody! Mam dzisiaj dobry humor, więc sam zdecydujesz gdzie będziesz służyć!”. Rekrut rozpromienia się i mówi nieśmiało: „Zawsze chciałem zobaczyć Stany…”. Na co sierżant: „Mówisz – masz! Piszemy: A-F-G-A-N-I-S-T-A-N”.
Teraz popita i jeśli suchar przeszedł to lecimy z tematem.
„Stan” o którym będziemy dzisiaj rozmawiać należy do rodziny wzorców strategio-podobnych, dlatego zaczniemy od przypomnienia sobie pozostałych i porównamy je z bohaterem tego tekstu. Opisy są skrótowe i skupiają się na najważniejszych różnicach.
- Strategia – pozwala wybrać JEDEN Z WIELU algorytmów do uzyskania potrzebnych danych
- Fabryka – pozwala wybrać JEDEN Z WIELU algorytmów do utworzenia zestawu konkretnych obiektów
- Metoda szablonowa – pozwala stworzyć kilka algorytmów w oparciu o wspólny szablon, następnie korzystamy z JEDNEGO, dobranego do sytuacji
- Polecenie – pozwala wybrać TYLE ILE CHCEMY algorytmów do dowolnego celu
- Stan – jak powyżej, ale my tylko wybieramy początkowy algorytm, który zmienia się na kolejny wedle wcześniej ustalonych zasad
Dokładna definicja brzmi tak:
Stan – umożliwia obiektowi zmienianie zachowania wraz ze zmianą jego wewnętrznego stanu. Uzyskujemy w ten sposób złudzenie zmiany klasy tego obiektu.
Rusz Głową! Wzorce projektowe
O ile taką Strategię wybieramy sami, o tyle decydując się na wzorzec Stan idziemy w automatykę. Stany muszą być co najmniej dwa: np. włączony i wyłączony, a każdy powinien mieć metodę pozwalającą na przejście do tego drugiego.

Jeśli weźmiemy za przykład latarkę to ma ona dwa stany – albo jest zaświecona, albo zgaszona. Przycisk jest interfejsem, który zmienia stan latarki – z zaświeconego na zgaszony i z powrotem.
TEN SAM PRZYCISK ROBI DWIE RÓŻNE RZECZY w zależności od stanu w jakim znajduje się latarka.
Stanów może być oczywiście więcej, mogą się rozgałęziać, iść w dziesiątki, albo wracać do początku. Tylko pamiętajmy, że każdy stan jest reprezentowany przez osobną klasę dlatego nie ma co przesadzać – chyba, że chcemy sobie urządzić śmietnik z naszego projektu.
Ale po kolei…
Przeglądam właśnie swoją Steamową bibliotekę i zastanawiam się który końcowy boss wywarł na mnie największe wrażenie.
Końcówka Mass Efect (1) była w iście filmowym stylu i wciąż od czasu do czasu puszczam sobie soundtrack jej towarzyszący. Ale boss nie był jakoś szczególnie wymagający.
Divine Divinity Original Sin oferowało miodną rozgrywkę i pamiętam z niej przepiękne lokacje, ale nie ostatecznego złodupca. Najwidoczniej nie był wart zapamiętania…
Mam! FTL: Faster Than Light! Grę opisałem w osobnym artykule, jeśli ktoś nie grał to gorąco zachęcam! Latamy sobie stateczkiem po kosmosie (turowo), a od czasu do czasu toczymy bitwy z innymi załogami. Jedna rozgrywka trwa do dwóch godzin i jeśli uda nam się dotrwać do końca to przychodzi czas na walkę z ostatecznym bossem – ogromnym statkiem rebeliantów.

Bitwa jest szczególnie wymagająca, a to przez ten detal, że w odróżnieniu od poprzednich wrogów, końcowy boss ma trzy stadia ataku do których musimy się dostosować. Na początku wróg próbuje hakować nasze systemy ofensywne i defensywne, wyłącza osłony i atakuje bronią konwencjonalną.
Kiedy uda nam się uszczuplić jego pasek zdrowia, następuje Zmiana Stanu – teraz krążownik zaczyna wysyłać ku nam autonomiczne drony. Jeśli nie mamy dobrze zorganizowanej obrony to szybko potną nas na kawałki.
Gdy wrogiemu okrętowi zostanie mniej niż 30% wytrzymałości znów przechodzi w inny stan – tym razem teleportuje do nas roboty, których celem jest eliminacja naszych załogantów – próbuje zniszczyć gracza od środka.

Jak widać, zmiana stanów jest uzależniona od punktów życia – przy każdych nowych obrażeniach następuje sprawdzenie czy już należy zmienić stan.
Przygotowałem coś podobnego (pełny kod tutaj) ale jak dotychczas – w klimatach fantasy. Zacznijmy od analizy walki wojownika z bossem:

Boss ma trzy stany/fazy, a każdy z nich jest osobną klasą implementującą interfejs Phase. Ten zaś posiada tylko jedną metodę: retaliate() która jest odpowiedzią na atak wojownika i która w każdej fazie bossa działa nieco inaczej.

W fazie pierwszej obrażenia są mierne, a boss nie korzysta z żadnych umiejętności specjalnych. Kiedy punkty życia (bossa) spadną poniżej 150 następuje zmiana stanu w klasie bossa – od teraz będą wykorzystywane metody fazy drugiej.
if (user.getHp() < 150){
user.setPhase(new PhaseTwo());
}


Ostateczny etap to potężny atak skillowy. Ten stan już nie prowadzi nigdzie dalej. Skończy się sam jeśli tylko warunek (hp <= 0) w klasie bossa zostanie spełniony.
Fajne jest to, że stany zmieniają się automatycznie, dzięki czemu cała, długa potyczka wywoływana jest jedną komendą:
warrior.attack(bigBoss);
ITCandidateEvaluator
Podsumowując.
Wszystkie „strategiczne” wzorce wydają się dobrze przemyślane i przydatne, ale jeśli miałbym wybrać tylko jeden z nich to zdecydowałbym się na Metodę Szablonową (z Fabryką na miejscu drugim). Jest to najbardziej uniwersalne rozwiązanie, które sprawdzi się w chyba każdym projekcie, a do tego (w porównaniu z bazową Strategią) ogranicza duplikację kodu.
Jeśli chciałbym użyć „Polecenia” to musiałbym je wciskać trochę na siłę, specjalnie zmieniając strukturę klasową tylko pod to podejście. A to chyba powinno działać na odwrót, prawda?
Natomiast co do Stanu – o ile bardzo mi się spodobał, to na tę chwilę po prostu nie widzę możliwości aby go użyć, tak by mi w czymś pomagał. Obiekt kandydata cały czas będzie miał stan „niezatrudniony”, a jego zmiana może nastąpić tylko poza ramami programu.
To byłoby na tyle. Dziękuję za uwagę – w następnym wpisie zajmiemy się wzorcem „Proxy„.