• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Formatowanie kodu

Object Storage Arubacloud
0 głosów
541 wizyt
pytanie zadane 19 lipca 2020 w Java przez msky Początkujący (250 p.)

Cześć,

mam pytanie co do formatowania kodu, tworzenia klas, rozdzielania kodu. Mianowicie, jak i czym się kierować, żeby kod był przejrzysty i elegancki. Chciałbym tutaj prosić bardziej doświadczonych programistów o wskazówki. 

Dla przykładu tutaj podrzucam kod do kółka i krzyżyk w Javie: https://github.com/kmsky/tictactoe-with-minimax

Co należy dodać, zmienić, żeby zwiększyć przejrzystość kodu? Czy rozdzielanie kodu w dwóch lub więcej plikach to dobry nawyk czy zupełnie zbędny i lepiej trzymać wszystko w jednym pliku.

Ewentualnie prosiłbym o wskazanie jakiejś literatury z której taką wiedzę mógłbym pozyskać.

Dzięki i pozdrawiam :)

1 odpowiedź

+1 głos
odpowiedź 19 lipca 2020 przez Lewo Gaduła (3,000 p.)
wybrane 19 lipca 2020 przez msky
 
Najlepsza

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
 

1
komentarz 19 lipca 2020 przez Lewo Gaduła (3,000 p.)
Literatura
https://helion.pl/ksiazki/czysty-kod-podrecznik-dobrego-programisty-robert-c-martin,czykov.htm#format/d
A jeśli chodzi o architekturę aplikacji, jak ją dzielić itd to troszkę szerszy temat. Zainteresuj się na początek: aplikacje trójwarstwowe.
Więcej można poczytać w dośc wiekowej książce
https://helion.pl/ksiazki/architektura-systemow-zarzadzania-przedsiebiorstwem-wzorce-projektowe-martin-fowler,szabko.htm#format/d
komentarz 19 lipca 2020 przez msky Początkujący (250 p.)
Dzięki za naprawdę wyczerpującą odpowiedź. Spróbuję to wszystko przetrawić i ulepszyć kod.

"nie kopiuj stanu do Minmax.java np. boardu, użytych znaczków dla playerów itd. To powinno być w jednym miejscu"

Zakładając, że zastosuję zaproponowany przez Ciebie podział klas i chcąc, żeby inne klasy miały dostęp np. do gameboardu w klasie Board. Lepszym rozwiązaniem będzie ustawienie modyfikatora dostępu public czy stworzenie metody umożliwiającej dostęp, czy zrobić to w jeszcze inny sposób?
1
komentarz 19 lipca 2020 przez Lewo Gaduła (3,000 p.)

Jeśli GameService tworzy instancję Board, a AI potrzebuje wiedzieć jaki jest układ w danym momencie to może pobrać Board do jakiejś swojej metody, ale nie powinien przechowywać na stałe u siebie. Board będzie polem prywatnym w GameService (by np. AI nie zmienił czegoś np nie wstawił X/O - to robi GameService) i będzie publiczny getter do tego pola.

Ogólnie do przemyślenia jest w jaki sposób Player wykonuje ruch.
Ja bym się zastanowił nad opcją, że definiujesz interface MoveMaker czy coś z metodą do zaimplementowania Field makeMove(Board gamePosition). Player implementuje taki interfejs w ten sposób, że pobiera z konsoli pole gdzie postawić znak. AI może dziedziczyć po Player i przesłonić metodę makeMove w taki sposób, że nie pobiera pola, tylko wylicza. Finalnie metoda makeMove zwraca Field na jaki stawia X. GameService ma w dupie jak ktoś wykonał ruch (nie musi decydować czy pobieramy teraz znak czy odpalamy minmax), po prostu wywołuje makeMove na graczy którego jest tura.

Dam takie ciekawe porównanie które ostatnio usłyszałem
Gdy chcesz nakarmić człowiek to nie robisz w klasie
czlowiek.getUkladTrawienny().getBrzuch().add(kielbasa)
ten kto posiada instancję człowieka nie powinien wiedzieć jakie "bebechy" ma człowiek, tylko wywołać metodę czlowiek.eat(kielbasa)

Podobne pytania

0 głosów
2 odpowiedzi 97 wizyt
pytanie zadane 24 stycznia w HTML i CSS przez whiteman808 Obywatel (1,820 p.)
0 głosów
1 odpowiedź 484 wizyt
pytanie zadane 14 maja 2017 w Systemy operacyjne, programy przez piotrsz109 Stary wyjadacz (13,730 p.)
0 głosów
0 odpowiedzi 84 wizyt
pytanie zadane 21 listopada 2020 w Systemy operacyjne, programy przez domiinikkaaa Nowicjusz (120 p.)

92,568 zapytań

141,424 odpowiedzi

319,634 komentarzy

61,956 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...