Poczytaj https://pl.wikipedia.org/wiki/Zasada_jednej_odpowiedzialno%C5%9Bci
- Main ma za dużo odpowiedzialności (startowanie gry, przechowywanie stanu gry, model graczy, wyświetlanie stanu)
- Przez to wszystko jest statyczne, nie ma za bardzo enkapsulacji
- Metody powinny być w większości private a klasa która steruje innymi obiektami powinna mieć tylko parę do wyboru
- Komentarze "nie są szminką" dla złego kodu. Tzn. nie opisujesz komentarzami co robi dana rzecz, tylko nazwy metod i zmiennych mówią co się dzieje. Komentarze możesz zostawić tam gdzie nie da się tego dobrze opisać kodem.
Ogólnie Twoje aplikacja jest mała, więc da się zorientować, ale jak chcesz się nauczyć pisać porządnie to bym użył jakiegoś podziału np.
klasy: Board , Player, GameService, AIService, ConsoleView
Board
- wewnętrznie może być to dwuwymiarowa tablica, ale korzystający nie powinien wiedzieć w jaki sposób numerować pola. Ja bym radził oprócz klasy Board zrobić klasę Field która ma dwie współrzędne: x, y. Dodatkowo w klasie Board zrobić publiczne statyczne stałe, które będą miały nazwy od kierunków świata, żeby nie domyślać się czy pole 1 numeruje się od lewego-górnego czy dolnego rogu, czyli
Board {
private Field[] fields;
public static final Field N = new Field(0, 1), NE = new Field(0, 2), ...
public boolean mark(Field toMark) {}
// zwraca czy udalo sie oznaczyć (nie udać się moze gdy pole zajęte, lub podane pole ma dziwne kordynaty np. -1, 5
// chociaż bardziej polecam zamiast boolean wyrzucić błąd który poinformuje o tym, że pole jest zajęte
// reszta metod powinna być raczej prywatna (może ewentualnie metoda publiczna zwracająca czy koniec gry)
}
Field {
private int x, y;
// konstruktor z dwoma parametrami i gettery
}
Player ma np. informację o przypisanym znaku i czy to bot(x czy o ale możesz dowolnie rozszerzać jego dane np. o nick, ranking itd), sam znak x lub o powinno być stałą, najlepiej enumem. GameService zarządza przebiegiem rozgrywki (tury graczy, sprawdzanie końca gry, wywołanie po ruchu ConsoleView by wyswietlił stan Boardu)
Z perspektywy GameService AI to zwykły Player który w inny sposób podaje wykonywane ruchy (zamiast przez konsolę przez podanie pola, to zwróci nam Field metoda która opiera się na minmax). Player ani AIService nie powinny przechowywać w swojej klasie informacji o Board (Board powinnien być w jednym miejscu, byś potem nie miał różnych stanów Board w różnych klasach).
- jak masz nie intuicyjne warunki w ifie to zamiast pisać komentarz wepchnij je to metody boolean o wymownej nazwie
- nie kopiuj stanu do Minmax.java np. boardu, użytych znaczków dla playerów itd. To powinno być w jednym miejscu
- nie intucyjne położenie metody checkWinner w klasie Minmax.java (ta klasa imo powinna służyć do wybrania optymalnego ruchu dla kompa a nie sprawdzać kto wygrał -> tutaj znowu zasada pojedynczej odpowiedzialności)
- jak wprowadzisz klasę Board to zamiast initializeBoard zrobisz to przez konstruktor
- metoda main() powinna ograniczyć się do uruchmienia jakiejś klasy service która utworzy grę i zainicjalizuje