Jak podzielić projekt na klasy?

1

Po poznaniu jakichś tam podstaw C zabrałem się za C#. Obiektowy, efektowny, może niezbyt szybki ale przynajmniej prosty.
Równolegle do czytania Troelsena chciałem się zabrać do pisania jakichś małych rzeczy, które zmusiły by mnie do zapoznawania się z dokumentacją języka i były by fajne ;). Gomoku, warcaby, statki, jakieś karciane gry, szachy, tego typu zabawy.
Najpierw zauważyłem, że rozrasta mi się głównie jedna klasa, kilkaset linii, powoli zaczynam się gubić co jest gdzie. Więc decyzja - trzeba to jakoś podzielić, tak trzeba było zrobić na samym początku. Wyróżnić obiekty (plansza, pion, karta, gracz...), powiązać je ze sobą, a klas dziedziczących po Form używać tylko do wyświetlania wyników.
I tutaj pojawia się problem z jakim się do Was zwracam. Zacząłem się zastanawiać czym poszczególne obiekty winny się charakteryzować, jakie działania podejmować, gdzie należeć (np.: plansza ma pole), czym być (np.: pion jest figurą). Nie potrafię się jednak zdecydować na zakres odpowiedzialności konkretnych obiektów...
*Czy karta wie u którego gracza jest w ręce?
*Czy dana figura wie, na jakie pola może się ruszyć, czy raczej plansza powinna to wiedzieć?
*Czy metoda Ruch, załóżmy o sygnaturze Ruch(Pole skad, Pole dokad) powinna aktualizować zarówno logikę, jak i ekran (wtedy trzeba by jej przekazać adres do obiektu dziedziczącego po Form na którym dzieje się malowanie)? Jeżeli nie, to jak uniknąć przemalowywania wszystkiego, zmieniając tylko to co konieczne?
Takich pytań mógłbym mnożyć, ale nie o to chodzi. Nie chcę dostać na nie odpowiedzi, tylko dowiedzieć się jak odpowiadać na nie samemu. Nie potrafię problemu sformułować zwięźlej niż: Jakie obiekty powinien zawierać mój projekt, czym mają się one charakteryzować i co robić.

4
Bateria napisał(a):

*Czy karta wie u którego gracza jest w ręce?

A pytałeś kiedyś jakiejś karty o to? Mi żadna nigdy nie odpowiedziała.

*Czy dana figura wie, na jakie pola może się ruszyć, czy raczej plansza powinna to wiedzieć?

Nie wie tego ani bierka (figury i piony to bierki, piony nie są figurami) ani plansza. To wie Gra na podstawie słownika reguł ruchu dla każdej bierki oraz historii gry. Oczywiście możesz zrobić tak, że to bierka zwraca Ci reguły swojego ruchu, albo wylicza pola, na które może się ruszyć z pola o danych współrzędnych, ale to nigdy nie będzie końcowa informacja. (Bo np. bierka nie wie, gdzie stoją inne bierki.)

*Czy metoda Ruch, załóżmy o sygnaturze Ruch(Pole skad, Pole dokad) powinna aktualizować zarówno logikę, jak i ekran (wtedy trzeba by jej przekazać adres do obiektu dziedziczącego po Form na którym dzieje się malowanie)? Jeżeli nie, to jak uniknąć przemalowywania wszystkiego, zmieniając tylko to co konieczne?

Logika i prezentacja powinny być oddzielone, metoda Ruch powinna aktualizować jedynie planszę, a warstwa prezentacji powinna umieć narysować planszę. Bezwzględnie nie powinno być powiązania między logiką a GUI, a już zwłaszcza nie przez jakieś Formy.
Jeśli chcesz nie przemalowywać całej planszy, to logika prezentacji powinna pamiętać poprzednia planszę i umieć znaleźć różnice między poprzednią, a nową i je inteligentnie narysować.

Takich pytań mógłbym mnożyć, ale nie o to chodzi. Nie chcę dostać na nie odpowiedzi, tylko dowiedzieć się jak odpowiadać na nie samemu. Nie potrafię problemu sformułować zwięźlej niż: Jakie obiekty powinien zawierać mój projekt, czym mają się one charakteryzować i co robić.

Jak będziesz pisał programy, to będziesz spotykał różne problemy, będziesz je rozwiązywał, a potem będziesz stwierdzał, że jedne pomysły są dobre, a inne złe. To się nazywa doświadczenie.

0

zrob tak aby bylo wszystko przejrzyste i w przyszlosci mozna byloby sie latwo polapac co jest czym . Chyba taki jest glowny sens programowania obiektowego. Kazdy ma swoje reguly, nie ma scislych wytycznych. Tylko tez nie przesadzaj z iloscia klas :P Czasem mnie smieszy jak widze mase klas i zastawniam sie jaki jest sens tego

0
somekind napisał(a):

Logika i prezentacja powinny być oddzielone

Ale wtedy tą samą robotę robię dwa razy. Raz - żeby zmienić logikę, a potem jeszcze raz, żeby się zorientować co się zmieniło. No ale rozumiem, że przeważają tu inne względy, zwłaszcza że piszesz "bezwzględnie" ;).
Zdaję sobie sprawę z tego, że nie ma uniwersalnych rozwiązań, potrzebne jest po prostu doświadczenie, ale co polecilibyście człowiekowi bez doświadczenia? ;) Dobre praktyki? Może jakieś case study, gdzie ktoś nie tylko pisze ale i tłumaczy dlaczego napisał tak a nie inaczej? A może nie patrzyć na nic, nie czytać, tylko pisać, pisać, pisać? Ale w takim wypadku, jeżeli się po kilkuset liniach kodu zorientuję, że wszystko trzeba było zaprojektować od drugiej strony - cofać się, czy doprowadzać taki koślawy projekt do końca?
A Wy, jak sobie na początku radziliście, co robiliście, co się działo między pustą kartką a momentem kiedy diagram klas był już, w zarysie, gotowy, choćby i w głowie tylko?

0

mysle ze zle do tego podchodzisz. Nie powineies na poczatku zawracac sobie tym glowym. Po prostu rob swoje a z czasem sam bedziesz wyciagac wnioski. Skup sie na konkretnych problemach a nie jakie miec nawyki itp. W ogole jestes pewny ze chcesz byc programista? Masz pasje do tego?

0
Bateria napisał(a):
somekind napisał(a):

Logika i prezentacja powinny być oddzielone

Ale wtedy tą samą robotę robię dwa razy. Raz - żeby zmienić logikę, a potem jeszcze raz, żeby się zorientować co się zmieniło. (...)

Co masz dokładnie na myśli?

1
Bateria napisał(a):

Ale wtedy tą samą robotę robię dwa razy. Raz - żeby zmienić logikę, a potem jeszcze raz, żeby się zorientować co się zmieniło.

Dlaczego dwa razy? Zmiana logiki to zmiana jakichś wartości w obiekcie opisującym planszę gry, dopisanie nowej pozycji w historii gry. To się nie ma nijak do rysowania na ekranie.

No ale rozumiem, że przeważają tu inne względy, zwłaszcza że piszesz "bezwzględnie" ;).

Głównie zasada SRP. Jedna klasa powinna robić jedną rzecz. Jeśli jedna klasa ma odpowiadać jednocześnie za logikę i za wyświetlanie na ekranie, to znaczy, że będzie bardzo trudna w utrzymaniu i debugowaniu, oraz będzie wyglądała jak spaghetti.

Zdaję sobie sprawę z tego, że nie ma uniwersalnych rozwiązań, potrzebne jest po prostu doświadczenie, ale co polecilibyście człowiekowi bez doświadczenia? ;) Dobre praktyki? Może jakieś case study, gdzie ktoś nie tylko pisze ale i tłumaczy dlaczego napisał tak a nie inaczej?

Zapoznaj się z pojęciami: SOLID, KISS, DRY.
Dwie książki: Head First Object-Oriented Analysis and Design oraz Head First Design Patterns. Nie są to typowe książki, ale opisują proces poprawiania kodu. Do tego możesz dorzucić Clean Code.

A może nie patrzyć na nic, nie czytać, tylko pisać, pisać, pisać? Ale w takim wypadku, jeżeli się po kilkuset liniach kodu zorientuję, że wszystko trzeba było zaprojektować od drugiej strony - cofać się, czy doprowadzać taki koślawy projekt do końca?

Tak, cofnij się i popraw, i rób to, dopóki będziesz zadowolony z efektu.

0
Bateria napisał(a):
somekind napisał(a):

Logika i prezentacja powinny być oddzielone

Ale wtedy tą samą robotę robię dwa razy. Raz - żeby zmienić logikę, a potem jeszcze raz, żeby się zorientować co się zmieniło. No ale rozumiem, że przeważają tu inne względy, zwłaszcza że piszesz "bezwzględnie" ;).
Zdaję sobie sprawę z tego, że nie ma uniwersalnych rozwiązań, potrzebne jest po prostu doświadczenie, ale co polecilibyście człowiekowi bez doświadczenia? ;) Dobre praktyki? Może jakieś case study, gdzie ktoś nie tylko pisze ale i tłumaczy dlaczego napisał tak a nie inaczej? A może nie patrzyć na nic, nie czytać, tylko pisać, pisać, pisać? Ale w takim wypadku, jeżeli się po kilkuset liniach kodu zorientuję, że wszystko trzeba było zaprojektować od drugiej strony - cofać się, czy doprowadzać taki koślawy projekt do końca?
A Wy, jak sobie na początku radziliście, co robiliście, co się działo między pustą kartką a momentem kiedy diagram klas był już, w zarysie, gotowy, choćby i w głowie tylko?

Z doświadczenia Ci mówię, że jak nie rozdzielisz logiki od prezentacji to stracisz 2x tyle czasu na szukaniu błędu w kodzie.
Co do różnic między poprzednim widokiem a kolejnym - odświeżaj cały ekran. Dopóki odmalowaywanie nie będzie Ci wpływało na szybkość programu to nie ma co sobie tym zawracać głowy. Jak chcesz to ładnie zrobić to możesz w grze zapamiętywać listę ostatnich ruchów. Będziesz miał jednocześnie historię i będziesz wiedział co odświeżać. Innym rozwiązaniem jest wykorzystanie zdarzeń: widok rejestruje, że chce otrzymywać informacje o zmianie stanu, a logika informuje, że coś takiego zaszło.

0

Co do różnic między poprzednim widokiem a kolejnym - odświeżaj cały ekran. Dopóki odmalowaywanie nie będzie Ci wpływało na szybkość programu to nie ma co sobie tym zawracać głowy.

A niech sobie pozawraca tym głowę - czegoś sensownego się nauczy przy okazji :]

Lepiej od razu przyjąć sobie za cel odmalowywanie tylko tych regionów, które są wymagane do zaktualizowania; Przesunęła się bierka - odmalować dwa pola; Gracz zaznaczył bierkę - odmalować tylko pole bierki, ewentualnie także te pola, na które zaznaczona bierka może się udać; Lepiej od razu się tym zainteresować, bo później trzeba będzie przerabiać sporą część kodu, aby wprowadzić zoptymalizowane aktualizowanie ekranu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1