Press "Enter" to skip to content

Wzorce projektowe – Strategia

Wstęp i definicja

Wzorzec projektowy Strategia jest jednym z wzorców czynnościowych. Definiuje on rodzinę algorytmów, które wykonują podobne zadania oraz mogą one być wykonywane wymiennie podczas działania aplikacji, niezależnie od klienta.

„Wzorzec Strategia definiuje rodzinę algorytmów, pakuje je jako osobne klasy i powoduje, że są one w pełni wymienne. Zastosowanie tego wzorca pozwala na to, aby zmiany w implementacji algorytmów przetwarzania były całkowicie niezależne od strony klienta, który z nich korzysta.”

Definicja zaczerpnięta z książki Rusz głową! Wzorce projektowe

Gra karciana

Wyobraźmy sobie, że piszemy grę karcianą, w której każda z kart zadaje przeciwnikowi obrażenia i zabiera pewną ilość zdrowia.

Struktura kart

W podstawowej wersji gry mamy dostępne 2 rodzaje kart – Rycerza oraz Katapultę. Oczywiście nic nie stoi na przeszkodzie, aby dodać więcej rodzajów kart do naszej gry, wystarczy rozszerzyć klasę Card o nowy podtyp oraz przesłonić metodę attack().
Właśnie wpadliśmy na pomysł, aby do naszej gry dodać możliwość ostrzenia mieczy (w międzyczasie doszedł nam nowy rodzaj karty).

Nowa struktura kart

Dodaliśmy nową funkcjonalność do klasy nadrzędnej Card. Możemy teraz zauważyć, że nadaliśmy właściwość ostrzenia miecza do… KATAPULTY! Co teraz?
Oczywiście możemy nadpisać implementację metody bladeOfTheSword() w klasie Catapult. Ale co, jeśli dodamy do projektu kolejny typ kart, na przykład Okręt? Okręt również nie może naostrzyć miecza. Ciekawym rozwiązaniem wydaje się zastosowanie interfejsów oraz ich implementacja w klasach pochodnych typu Card. Dzięki temu będziemy mogli określić, które karty mogą ostrzyć miecze, a które nie mogą tego robić. Jest to oczywiście jakieś rozwiązanie, ale nie najlepsze, ponieważ pozbywamy się możliwości ponownego wykorzystania kodu, ponieważ wiele algorytmów będzie powtarzało się w różnych typach kart.

Oddzielenie zmieniających się fragmentów aplikacji

W przypadku naszej gry, da się wyodrębnić metodę, która zmienia się w zależności od pewnych typów kart – jest to metoda bladeOfTheSword().

Struktura klas z nadpisaną metodą bladeOfTheSword()

A co jeśli taką metodę do ostrzenia mieczy potraktowalibyśmy jako coś specjalnego? Co jeśli do talii kart dodamy nowy typ karty – maga, który potrafi atakować różnymi dodatkowymi umiejętnościami w zależności od sytuacji lub jego domyślnej konfiguracji?

Zastosowanie interfejsu

Do rozwiązania powyższego problemu, z pomocą przychodzą nam wcześniej wspomniane interfejsy! W tym przypadku, interfejsy możemy zastosować jako sposób do wydzielenia pewnej „akcji”, która będzie zmieniana w zależności od potrzeb w danej chwili.

Struktura interfejsu akcji

Wyodrębniliśmy właśnie wykonywanie specjalnych ataków (podpalenie, krwawienie, ostrzenie miecza) do osobnych klas, które będą opisywały te ataki. Dzięki takiemu rozwiązaniu, jesteśmy w stanie oddzielić część implementacji związaną z wykonywaniem ataków różnych dla każdego typu karty. Dodatkowo, jeśli przyjdzie sytuacja, w której będziemy chcieli zmienić zachowanie ataku wywołującego krwawienie u przeciwnika, będziemy musieli zmienić implementację metody makeSpecialAttack() w klasie Bleeding.
Aby połączyć to w jedną działającą całość, wystarczy teraz utworzyć zmienną typu SpecialAttack w klasie Card, napisać metodę, która będzie uruchamiała wykonanie specjalnego ataku oraz napisać metodę do zmiany ataku podczas działania programu, dzięki czemu będziemy mogli dynamicznie zmieniać zachowanie aplikacji.

Klasa Card

Finalna wersja gry

Po wyodrębnieniu wszystkich zmieniających się rzeczy od rzeczy stałych, posiadamy projekt aplikacji, która będzie łatwiejsza podczas implementacji nowych ataków specjalnych oraz podczas zmieniania już istniejących.
Na koniec schemat przedstawiający połączenie wszystkich części aplikacji w jedną logiczną całość. Kod do tej części tutoriala znajduje się na githubie.

Finalna struktura klas

Przykład uruchomienia aplikacji:

Kod źródłowy klasy uruchamiającej

Wyjście:

Wyjście aplikacji

Na koniec życzę wszystkim powodzenia i do przeczytania! 🙂