Każdy kto kiedykolwiek uczył się programowania wie doskonale, że różnica pomiędzy wykonaniem zadania testowego z kursu, a napisaniem własnej aplikacji „od podszewki” jest kolosalna. Dziś podzielę się z wami historią jak wyglądało tworzenie pierwszego samodzielnego projektu w moim wypadku.
Zadania z kursu CodeGym o którym wspominałem wcześniej dzieliły się pod względem złożoności na dwa typy: małe i duże. W tych małych mieliśmy zazwyczaj jedną klasę z dwiema metodami – pierwsza korzystała z drugiej, natomiast ta druga była pusta, a do nas należało takie jej kodowanie aby zwracana wartość była prawidłowa.
Duże zadania (które zaczęły się od poziomu 20+) polegały z kolei na „własnoręcznym” napisaniu prostej aplikacji. Były to np. gry takie jak 2048, Tetris czy Sokoban albo aplikacje funkcyjne typu agregator ofert pracy, archiwizator plików lub program symulujący pracę restauracji. Słowo „własnoręcznym” celowo ująłem w cudzysłów gdyż tak de facto, po prostu krok za krokiem wypełnialiśmy polecenia kursu:
- stwórz klasę taką a taką, musi dziedziczyć to i implementować ten interfejs;
- utwórz zmienną o nazwie X, musi być publiczna i statyczna;
- zaimplementuj metodę która sprawdza coś tam gdzieś tam;
Jakiekolwiek odstępstwa od normy były karane niezaliczeniem zadania, więc trzeba było wszystko robić tak jak twórca projektu to zamyślił. Na domiar złego niektóre klasy/metody były automatycznie pobierane z serwera kursu i dodawane do „naszej” aplikacji. Następowało to wtedy kiedy twórca zdania uznał, że dany element nie został dobrze wyjaśniony i nie ma takiej potrzeby, bo np. jest już przestarzały (jak biblioteki Swing do tworzenia prostej grafiki).

Czyli niby robiliśmy sami, ale koniec końców nie wiedzieliśmy dlaczego to ma akurat tak wyglądać. Kiedy w końcu zabrałem się za stworzenie CAŁKOWICIE SAMODZIELNEGO projektu miałem już w głowie jego zarys, ale uruchomiwszy IntelliJ Idea ręce zamarły mi nad klawiaturą, a w głowie kotłowało mi się jedno pytanie:
Od czego zacząć?
Biorąc przykład z tych dużych zadań na CodeGym postanowiłem oprzeć projekt na architekturze MVC zatem na szybko stworzyłem pakiety i odpowiednie klasy: Model, View, Controller. Co teraz?
Mój projekt bazował na minigierce podejrzanej w grze mobilnej Rise of Castles w którą do niedawna się zagrywałem. Nie będę linkował bo to szajs z gatunku PayToWin w który nieopatrznie dałem się wciągnąć i jako darmowy użytkownik byłem dymany z każdej strony przez ludzi, którzy wydali tam więcej kasy niż ja na swoją starą Toyotę.
Tym niemniej miło wspominam właśnie swojego rodzaju puzzle które pojawiły się w tej grze. Zasady były banalne, ale rozgrywka wymagająca:
Oto mamy planszę. Na tej planszy w regularnych stosach poukładane są klocki różniące się malunkami. Wszystkich klocków jest grubo ponad 300, a różne typy klocków (z tymi samymi obrazkami) mają różną liczbę kopii. Niektórych jest 30, a innych tylko 6. Element wspólny - ta liczba jest zawsze podzielna (bez reszty) przez 3.
Naszym zadaniem jest wyczyścić planszę ze wszystkich klocków. Robimy to klikając na klocek - ta czynność przenosi go poniżej, do swoistej kieszeni. Klikać można tylko na klocki będące na szczycie stosu, nie przesłonięte przez żadne inne. Owa kieszeń zaś jest ograniczona i mieści maksymalnie 7 elementów, gdy się zapełni - przegrywamy. Gdy na planszy nie zostanie już ani jeden klocek - wygrywamy. Tyle.

Na początku próbowałem podejść do tematu backendowo od strony modelu: stworzyć planszę, kieszeń, klocki (które tu nazwałem po prostu kwadratami) i dodać im odpowiednie zachowanie. To się zwyczajnie nie sprawdziło – zabrakło mi wizualizacji tego co właśnie robię i co będzie mi potrzebne w następnej kolejności. Nastawiłem się na frontend i zaatakowałem od strony widoku (View).
Tutaj mała dygresja – doskonale zdaję sobie sprawę, że biblioteki Swing z których korzystałem odeszły dawno do lamusa. Ale jako, że miałem już z nimi do czynienia (w CodeGym oraz w książce Rusz Głową: Java) z pomocą Chat GPT udało mi się osiągnąć zamierzony efekt bez ładowania się w kurs nowego frameworku. Na to jeszcze przyjdzie czas.

Wracając do naszej historii.. Stworzyłem sobie okienko JFrame, w tym okienku stworzyłem planszę JPanel, dodałem tam jeden kwadracik JComponent oraz narysowałem kieszeń (również JComponent).
Teraz już widziałem co mam i co chcę z tym zrobić. Przede wszystkim kwadracik po kliknięciu miał przeskakiwać do pierwszego wolnego miejsca kieszeni. Po takim ruchu nie można było już go kliknąć. Kwadracików miało być znacznie więcej – dokładnie 360, do tego w różnych kolorach, ustawionych w różnych miejscach o współrzędnych x/y/z. W tym właśnie miejscu pojawiła się potrzeba popracowania nad modelem który „wyprodukuje” wszystkie potrzebne kwadraciki i nada im odpowiednie koordynaty z których następnie skorzysta View i narysuje je w odpowiednich miejscach. Pojawiła się też implementacja kontrolera (klasa Controller), który wysyłał modelowi informacje który kwadracik właśnie został kliknięty i odbierał feedback co z nim należy zrobić.

To o czym tutaj czytacie wygląda prosto, zupełnie jakby udało się to zakodować w dwa, góra trzy dni. Musicie jednak wziąć poprawkę na to, że próbowałem różnych elementów, traciłem dużo czasu na rzeczy które ostatecznie się nie sprawdzały i je z żalem usuwałem. Od pierwszej linijki kodu do momentu w którym uznałem projekt za zakończony minęły dwa tygodnie codziennej pracy od rana do wieczora. Ale co się przy tym nauczyłem to moje.
Nie udało mi się osiągnąć wszystkiego co chciałem. Dziwny błąd który sprawiał, że JVM zawsze „pamiętała” które klocki zostały już usunięte z planszy pokrzyżował mi plany z dodaniem przycisku „reset” rozpoczynającym rozgrywkę od początku. Straciłem cały dzień na kminienie dlaczego tak się dzieje, mimo, że za każdym wciśnięciem reset tworzę nowy Model, nowy View, nową planszę i nowe klocki. Bez skutku.
Pod koniec prac przeniosłem projekt z IntelliJ Community Edition na Ultimate zaktualizowanego do najnowszej wersji, co z niewiadomych przyczyn popsuło biblioteki JUnit a to równa się kolejny dzień poświęcony tylko na to aby rozprawić się z nowym problemem.

Kilka dni poszły na obczajanie jak podzielić się moją appką z resztą świata. Eksperymentowałem z Apache Tomcat i instancjami na Amazon Web Services. O ile Tomcat sprzężony z IntelliJ uruchamiał okienko z grą w mojej przeglądarce, o tyle ten na AWS-owym Linuksie w ogóle nie widział skryptu za to odpowiedzialnego. Było to rozczarowujące, ale też mnóstwo się przy tym dowiedziałem, a do tematu wrócę jak już ogarnę framework Springa.
Tym niemniej jestem cholernie dumny z tego co udało mi się osiągnąć, bo nie tylko stworzyłem swoją pierwszą kompletną aplikację, ale ta gierka jest zwyczajnie fajna i samemu lubię sobie w nią popykać na koniec dnia przy kawie. A ponadto sporo poćwiczyłem:
- czyste kodowanie, refaktoryzację
- budowanie zależności pomiędzy klasami i obiektami
- tworzenie testów w JUnit5
- tworzenie aplikacji okienkowych z użyciem Swinga
- niektóre wzorce projektowe (Singleton, Strategy, MVC)
- pracę z gitem/githubem
- budowanie projketu na Mavenie
- pierwszy raz otarłem się o AWS

Tyle na dzisiaj. Jeśli jednak ciekawi was jak wygląda kod mojego pierwszego samodzielnego projektu to zapraszam was na GitHuba oraz do kolejnego wpisu gdzie przyjrzymy się poszczególnym elementom pod względem zgodności z podstawowymi zasadami programowania takimi jak SOLID, DRY, KISS, YAGNI czy TDD.
