Nauka programowania to nie tylko składnia języka, środowisko, frameworki i API. Praca przy moim pierwszym samodzielnym projekcie uzmysłowiła mi jak ważna jest architektura i czysty kod. To w jaki sposób tworzymy naszą appkę – co jest gdzie i dlaczego akurat tam.
Tym wpisem chciałbym rozpocząć serię artykułów poświęconych najpopularniejszym wzorcom projektowym. Właśnie rozpocząłem lekturę książki „Rusz głową! Wzorce projektowe” i pragnąłbym aby wiedza tam zdobyta została ze mną jak najdłużej. Stąd w kolejnych tekstach możecie liczyć na krótkie przedstawienie danego wzorca oraz przykład jego zastosowania w osobiście przygotowanym na tę okazję kodzie. Ja sobie poćwiczę, a i wy może coś z tego wyniesiecie. Win-win.
Jak już wspominałem wcześniej – nie trafiają do mnie przykłady z użyciem zwierząt, dlatego tworząc kontekst posłużę się ponownie klasą czarodzieja. Jednakże, aby to jeszcze bardziej urozmaicić, nie będzie to byle jaki magik, a prawdziwy czarodziej ze świata DnD Forgotten Realms. Zaczerpnę z lore gry Baldur’s Gate której druga odsłona jest dla mnie pozycją kultową, która wywarła ogromny wpływ na kształtowanie mojej osobowości. Dość powiedzieć, że bez niej z pewnością nie byłbym tu gdzie jestem teraz*.
Wybrałem Zapomniane krainy również ze względu na bogactwo dostępnych zaklęć. Mamy tam nie tylko magię ofensywną, ale też polimorficzną, ochronną, nekromancję, regenerację, przyzywanie… – wręcz wymarzone poletko do przedstawienia wzorców i zabawy z nimi!

Zanim zaczniemy, jeszcze mała dygresja. Ktoś, kto obserwuje moją naukę programowania mógłby powiedzieć:
Chłopie! Gówno wiesz o Springu, o hibernejcie tylko z teorii. Co ja gadam! Przecież ty nigdy jeszcze własnego query w SQL-u nie napisałeś! A ty zamiast najpilniejszych spraw bierzesz się za wzorce projektowe! O Restowych API się doucz, a wzorce zostaw midom!
A ja uważam, że wręcz przeciwnie. Że, frameworki są fasadą (która nomen omen też jest wzorcem projektowym…) za którą kryją się podstawy języka. A podstaw należy się nauczyć na samym początku, by wyrobić sobie poprawne nawyki.
Wzorce stosujemy aby architektura naszego programu miała ręce i nogi, aby inni programiści patrząc na kod dokładnie wiedzieli gdzie co jest, co mogą zmienić, a co nie. W końcu, aby uczynić go elastycznym i gotowym na modyfikacje.
Dlatego lecimy z tematem:
Strategia
Strategy Pattern
Wzorzec strategii 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.
„Rusz Głową! Wzorce projektowe” (Eric Freeman, Elisabeth Freeman)
Jeśli nie zrozumieliście tego za pierwszym razem to… witajcie w klubie. Na szczęście ze Strategią już miałem parę razy do czynienia więc wiem jak to się gotuje i jak smakuje. Ostatni raz użyłem jej właśnie przy kodowaniu Clean Them All – stworzyłem tam kilka poziomów trudności, każdy w osobnej klasie. Konstruktor klasy modelu gry wyglądał tak:
public Model(Difficulty difficulty) {
allSquares = difficulty.getStrategy().getSquares();
}
Difficulty to klasa enum z czterema poziomami trudności, a każdy ma stałą „Strategy”. Strategy to interfejs który implementują klasy z poziomami trudności. Dzięki zastosowaniu Strategii, jeśli kiedykolwiek chciałbym dodać nowe poziomy trudności, wystarczy że stworzę taką klasę która implementuje Strategy i dodam ją do w/w enuma. Nic nie muszę zmieniać w klasie Model.
A teraz nowy przykład, już z czarodziejem. Pełna wersja kodu tutaj.
public static void main(String[] args) {
Wizard wizard = new Wizard();
Troll troll = new Troll();
wizard.attack(troll);
wizard.attack(troll);
System.out.println("Zmiana strategii");
wizard.spell = new FireArrow();
wizard.attack(troll);
}

Czy można było prościej? Oczywiście! Mogłem dodać oba czary bezpośrednio do klasy czarodzieja. Ale to byłoby niezwykle krótkowzroczne podejście. W Baldurze jest jakaś setka zaklęć – mam dodać te wszystkie metody? A wiecie, że Bard i Czarownik też znają magię? To, co? Kopiuj – wklej?
Za każdym razem kiedy chciałbym dodać nowy czar musiałbym modyfikować te trzy klasy, a to jak pamiętacie z poprzedniego wpisu, kłóci się z drugą zasadą SOLID: Otwarty na rozszerzenia, zamknięty na modyfikacje. Korzystając z wzorca strategii mogę dodawać każdej postaci zaklęcia w ogóle nie ruszając jego klasy.
ITCandidateEvaluator
Na zakończenie podzielę się z wami pomysłem na drugi samodzielny projekt. Będzie to aplikacja, którą może się posłużyć rekruter podczas rozmowy z kandydatem do pracy programisty.
Zaczyna się od otwarcia nowej rekrutacji albo załadowania tej już rozpoczętej. To samo z obiektem kandydata – kiedy ten zostanie stworzony i załadowany do widoku, rekruter przechodzi przez szereg elementów do oceny tj. znajomość języka angielskiego, pytania o projekty, o podstawy, o frameworki, umiejętności miękkie itd. Przy każdym elemencie rekruter wystawia ocenę, a na zakończenie algorytm przyznaje kandydatowi punktację wyciągając średnią. Po zakończeniu rozmów z wszystkimi chętnymi do pracy, rekruter może sobie podejrzeć całą ich listę i uszeregować od najlepszego do najgorszego z wykorzystaniem różnych kryteriów.
Chciałbym aby ta appka zachowywała osoby w bazie danych MySQL, a pytania wczytywała z pliku txt w odpowiednim katalogu. By zapisywała ścieżkę do CV każdej osoby aby na szybko można było te PeDe-Efy podejrzeć. Oraz, oczywiście aby korzystała z wzorców projektowych.
W związku z tym każdy wpis z tej serii będę kończył opisując jak danego wzorca użyłbym w moim projekcie. Jeśli chodzi o wzorzec strategii to sprawa jest prosta: sposób wczytywania/zapisu kandydatów. Mogę to zrobić dwojako: z wykorzystaniem bazy SQL oraz np. plików JSON. A w przyszłości mógłbym dodać też automatyczne generowanie listy np. poprzez wskazanie ścieżki do katalogu zawierającego CV – aplikacja sama sczytywała by nazwiska i np. wiek kandydatów, a następnie tworzyła ich rekordy w bazie danych.
Na dzisiaj tyle. W następnym wpisie zabierzemy się za wzorzec Obserwatora.
(*) a piszę te słowa siedząc u teściów w małej chińskiej wiosce