Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowości

Własne projekty oraz implementacje.

Moderator: xxSlayeRxx

Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowości

Sponsor

Sponsor
 

Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowości

Postprzez XardasLord » Pt lis 01, 2013 1:51 pm

Witam,

Dzisiaj udało mi się napisać grę znaną popularnie jako ‘Kółko i krzyżyk’. Chciałbym się nią z Wami podzielić i byłbym bardzo wdzięczny, gdybyście podzielili się swoją opinią na temat tej napisanej przeze mnie gry. Mam tu na myśli oczywiście Waszą opinię, Wasze uwagi pod kątem napisanego przeze mnie kodu.

Moim priorytetowym założeniem podczas pisania tej aplikacji było to, aby napisać ją w pełni obiektowo. Mówię to, ponieważ do tej pory miałem problemy, żeby przestawić się na pisanie obiektowe programów, sprawiało mi to trudność.

Także w załączniku przesyłam Wam plik .exe wraz z kodem.

Liczę na Wasze opinie, uwagi oraz mile widziana jest konstruktywna krytyka :)
Załączniki
Kolko_i_krzyzyk_ver.1.1.zip
(53.24 KiB) Pobrane 477 razy
XardasLord
Member
 
Posty: 14
Dołączył(a): Pt lis 01, 2013 1:48 pm

Re: Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowo

Postprzez DariuszR » Pt lis 01, 2013 3:27 pm

Słusznie nastawiasz się na obiektówkę :) Tyle że jeżeli chodzi o ten paradygmat programowania to musisz uświadomić sobie że wokół tego istnieje od dawna pewien zbiór zasad nazywany "dobrymi praktykami", który to niesamowicie ułatwia życie. Przede wszystkim:

SOLID (5 zasad):
http://www.pzielinski.com/?p=421
http://www.pzielinski.com/?p=422
http://www.pzielinski.com/?p=423
http://www.pzielinski.com/?p=424
http://www.pzielinski.com/?p=425

Zasada KISS:
http://pl.wikipedia.org/wiki/KISS_%28regu%C5%82a%29

Zasada DRY:
http://pl.wikipedia.org/wiki/DRY

Wzorce i antywzorce projektowe:
http://pl.wikipedia.org/wiki/Wzorzec_pr ... rmatyka%29
http://pl.wikipedia.org/wiki/Antywzorzec_projektowy

Generalnie co do samego kodu (przede wszystkim ze względu na prostotę i czytelność) nie za bardzo jest się o co przyczepić, chociaż z drugiej strony można by to rozwiązać nieco inaczej. Sam zdefiniowałbym jakiś silnik tej gry, przy czym druga osoba albo komputer byłyby po prostu odrębnymi klasami, implementującymi metody z jakiegoś interfejsu np. IPlayer. Dalej można by przyjąć że komputer uczyłby się w miarę kolejnych gier, co można by zaimplementować w ten sposób, że pole ustalone przez komputer jest nie losowe ale ściśle zależy od tego co wprowadził użytkownik.

Do wyświetlenia na ekranie można by zdefiniować osobną klasę a wprowadzanie znaków użytkownika można by podpiąć do jakiegoś kontrolera. Generalnie to sam wolałbym taką gierkę napisać w postaci graficznej i tak dla zabawy ją chyba napiszę, przy czym może właśnie z algorytmem uczenia dla komputera, później to ewentualnie tutaj przedstawię.
DariuszR
Member
 
Posty: 43
Dołączył(a): So paź 05, 2013 3:38 pm

Re: Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowo

Postprzez XardasLord » Pt lis 01, 2013 6:32 pm

Po pierwsze chciałbym bardzo podziękować za szybką i dość obszerną opinię na temat mojego problemu. Przeczytałem sobie wszystkie zasady i oczywiście wiem, że jeszcze nie raz będę do nich wracał :) Muszę od razu tu zaznaczyć, iż zauważyłem od razu w swoim kodzie łamanie niektórych zasad S.O.L.I.D. Przykładowo moja klasa Map posiada zbyt dużo różnie-funkcjonalnych metod (rysuje mape, wstawia podane znaki na mape, sprawdza czy uzytkownik wygrał bądź zremisował).

Jeśli dobrze rozumiem to przykładowo ta klasa Map powinna co najwyżej rysować mapę oraz może jeszcze dodatkowo wstawiała odpowiedni znak w odpowiednie miejsce (chociaż myślę, że nawet i tę funkcjonalność powinna realizować inna klasa, przykładowo silnik gry). Podobnie z metodą sprawdzającą stan gry (wygrana/remis) powinna być w tak zwanym silniku gry, czyli oddzielnej klasie. Dobrze rozumuję?

Myślałem właśnie nad tym, aby rozdzielić też zawodników. Mam tu na myśli oddzielną klasę dla gracza a oddzielną dla komputera. I właśnie stworzyć interfejs IPlayer, który zawierałby metody getField oraz putSign i ten interfest byłby implementowany przez te dwie klasy. Albo nawet metodę putSign wstawić do klasy odpowiedzialnej za silnik gry (czyli sprawdzanie stanu gry).

Jeśli chodzi natomiast o AI komputera to swój projekt cały czas rozwijam i następnym moim celem było właśnie dodanie jakiejś małej sztucznej inteligencji dla komputera, który to patrzałby na ruch gracza i odpowiednio reagował. Myślałem też jak to zrealizować i tutaj mam pewien dylemat. Bo do głowy przychodzi mi tylko sprawdzanie IFami odpowiedniej aktualnej kombinacji znaków na mapie i gdy taka kombinacja akurat zachodzi to wstawiać na 'sztywno' znak w jakieś pole. Ale tych ifów byłoby bardzo wiele i już teraz wiem, że będzie to trochę wbrew paradygmatom programowania obiektowego... Czytałem natomiast o algorytmie MINIMAX, ale zupełnie nie mam pojęcia jak go zastosować, gdyż rozumiem w miarę jego działanie, ale tam występuje rekurencja i gdy analizuję ten algorytm to po prostu tracę jego przejrzystość i przy rekurencji algorytmu nie wiem już co się w nim dzieje).

Na razie postanowiłem napisać to w konsoli, gdyż zależy mi tylko żeby nauczyć się pisać porządnie i w pełni wykorzystywać obiektowość. Z chęcią zobaczę tą gierkę, gdy zostanie napisana :)

Pozdrawiam
XardasLord
Member
 
Posty: 14
Dołączył(a): Pt lis 01, 2013 1:48 pm

Re: Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowo

Postprzez DariuszR » Pt lis 01, 2013 7:50 pm

Jeśli zastosujesz zgodność z SOLID, KISS, DRY program będzie czytelny, łatwy do rozbudowy a o to powinno chodzić. Generalnie zgodnie z SRP należałoby zrobić podział na klasy:

1. Interfejs IPlayer zawierający odpowiednie metody
2. Zaimplementować klasy gracza użytkownika/komputera z metodami z interfejsu IPlayer, potem przy wyborze z kim grasz tylko tworzysz odpowiednią instancję klasy
3. Zaimplementować klasę (coś w rodzaju kontrolera) do operacji IO (czyli podajesz miejsce gdzie wstawiasz krzyżyk, to idzie dalej gdzieś do innej klasy silnika gry silnik się komunikuje z klasami gracza/komputera i później w odpowiedzi daje dane do klasy wyświetlającej
4. Zaimplementować klasę do wyświetlania danych

Możesz przejrzeć mój projekt z sieci neuronowych który podałem tu w jednym wątku, tam zadbałem o zachowanie zasad SOLID, więc znajdziesz tam podział na wiele klas które czemuś służą. I w poprzedniej wypowiedzi chodziło mi właśnie o dobre praktyki.
DariuszR
Member
 
Posty: 43
Dołączył(a): So paź 05, 2013 3:38 pm

Re: Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowo

Postprzez XardasLord » Pt lis 01, 2013 8:28 pm

Postaram się napisać tą grę jeszcze raz od początku i tym razem uwzględniając wszystkie aspekty porady i przestrzegać zasad SOLID ale także KISS i DRY. Wiem, że z pewnością na początku nie będzie łatwo, ale chyba z niczym nie jest łatwo. Po prostu trzeba małymi kroczkami dążyć do osiągnięcia zamierzanego rezultatu i z takiego założenia wychodzę. Wiem, że jeszcze nie raz będę zgłaszał jakieś problemy :)

Mam jeszcze pytanie odnośnie głównej pętli gry. Ma się ona znajdować w Main? Czy też ma to być w osobnej klasie która to wszystko będzie przeprowadzać w odpowiedniej kolejności (w pętli oczywiście)?

Czyli rozumiem, że zadaniem silnika gry (czyli klasy) ma być:
1. Pobranie numeru pola z klasy kontroler i powiązanie go z odpowiednim graczem (user lub komputer) i sprawdzenie czy pole to jest puste, bo jeśli nie jest to ma zlecić funkcji w klasie kontroler ponowne pobranie znaku
2. Wysłanie tego pola i znaku do metody w klasie która będzie odpowiadała za wyświetlanie danych
3. Sprawdzanie stanu gry (czy ktoś wygrał, bądź czy padł remis)

Wydaje mi się że to główne założenia, które powinien realizować silnik. Choć piszę to teraz tylko tak na szybko a na pewno wszystko inne co potrzebne wyjdzie w praniu.

Zaraz zabieram się za przejrzenie Twojego projektu :)

Pozdrawiam
XardasLord
Member
 
Posty: 14
Dołączył(a): Pt lis 01, 2013 1:48 pm

Re: Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowo

Postprzez DariuszR » So lis 02, 2013 6:56 am

Powinieneś jeszcze zdefiniować klasę ConsoleApplication i użyć jej w głównej pętli main, jako że należy jeszcze użyć tzw. warstw. czyli np. tak.

Klasa ConsoleApplication:

Kod: Zaznacz cały
class ConsoleApplication
{
    //silnik gry
    Game game;

    public ConsoleApplication()
    {
    }

    public void Run()
    {
            int choice;
            String choiceString;
            try
            {
                do
                {
                     //tutaj tekst

                    choiceString = Console.ReadLine();
                    choice = int.Parse(choiceString);

                    switch (choice)
                    {
                        case 1:
                            {
                                game = new Game(new Player());
                                game.Play();
                                break;
                            }
                        case 2:
                            {
                                game = new Game(new Computer());
                                game.Play();
                                break;
                            }
                        case 0:
                            break;
                    }
                }    while(choice != 0 || choice < 1 && choice > 2);
        }
        catch (Exception E)
        {
            Console.WriteLine(String.Format("Wystąpił błąd w programie: {0}", E.Message));
        }
    }
}



i teraz w głównej metodzie main w klasie program:

Kod: Zaznacz cały
    class Program
    {       
        static void Main(string[] args)
        {
            ConsoleApplication application = ConsoleApplication();
            application.Run();
        }
    }



Istotna sprawa to try-catch, gdzie można wykrywać błędy runtime i logować je np. do jakiegoś pliku tekstowego. Oczywiście tutaj do zwalniania instancji klas zadziała GC :)
DariuszR
Member
 
Posty: 43
Dołączył(a): So paź 05, 2013 3:38 pm

Re: Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowo

Postprzez XardasLord » So lis 02, 2013 7:12 am

Okej wielkie dzięki teraz rozumiem :) Try-catch też znam, także wiem jak on działa i jak z niego skorzystać. Takie kontrolowane zabezpieczenie na wypadek niespodziewanych błędów (np litera zamiast numeru pola, itd.).

A dlaczego powinno się używać tzw. warstw? To znaczy dlaczego nie jest zalecane uruchomienie programu z poziomu main (mam na myśli, wstawienie do maina tej całej pętli która jest w metodzie void run)
XardasLord
Member
 
Posty: 14
Dołączył(a): Pt lis 01, 2013 1:48 pm

Re: Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowo

Postprzez DariuszR » So lis 02, 2013 8:26 am

Generalnie to ta zdefiniowana aplikacja jak tu podałem w przykładzie jest też obiektem (to tak jak samochód który składa się z silnika, skrzyni biegów, nadwozia itd), możesz jej nadawać właściwości (np. tytuł), definiować zdarzenia (używasz delegatów) np. OnIDLE, OnExit, OnException, więc masz tu pewne pole do manewru.

Oczywiście w razie wystąpienia jakichś błędów to jak debugujesz to poleci mniej więcej tak:

application.Run() -> game.Play() -> player.GetInputData() -> ...

tzn. działa to jakby po ścieżce podążając po niej można np. wskazać miejsce że akurat w klasie player masz jakiś błąd. Ale ogólnie chodziło mi tu o obiektowość, np. jak tworzysz aplikację okienkową (ja używam SharpDevelop) to właśnie stosowane jest takie podejście jak tu przedstawiłem (Application.Run()), i tam właśnie mogę sobie definiować zdarzenia, nadawać takiej aplikacji jakieś atrybuty itd.

Więcej można by powiedzieć jak napiszesz program w kolejnej wersji bo prawdę mówiąc to nawet nie chodzi o to abyś aż tak do przesady trzymał się reguł jak to żeby aplikacja była łatwa do rozbudowy (tak jak dobudowujesz jakiś garaż do nie rozwalasz całego mieszkania), kodu powinien być przejrzysty i czytelny a błędy łatwe do wykrycia.
DariuszR
Member
 
Posty: 43
Dołączył(a): So paź 05, 2013 3:38 pm

Re: Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowo

Postprzez XardasLord » So lis 02, 2013 7:31 pm

Mam pytanie jeszcze takie: czy gdy napiszę grę od nowa, trzymając się nieco bardziej zasad, tworząc przede wszystkim więcej klas, które mają sprecyzowaną rolę to czy taka wersja programu będzie łatwa do przeniesienia na okienka? Czy kod różni się diametralnie i będzie trzeba pisać wszystko od nowa? Pytam, gdyż nie pracowałem nigdy na okienkach w c# i jestem ciekaw jak to będzie.
XardasLord
Member
 
Posty: 14
Dołączył(a): Pt lis 01, 2013 1:48 pm

Re: Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowo

Postprzez xxSlayeRxx » So lis 02, 2013 8:26 pm

bedzie banalna...
zamieniasz tylko klase ConsoleInput, ConsoleOutput, na WindowInput,WindowOutput i masz gotowy program (input z naciskania klawiszy? na klikanie myszka, out z konsoli na okienko)

Z tym, jesli bedzie dobrze zrobiona gra (dobrze zaprojektowana), czyli zamiast

engine()
{
input = new input()
}

engine(IInput input)
{
input = input
}
a w IInput
{
int GetInput()
}

wtedy czy w konsoli zrobisz
var key = Console.ReadKey();
switch key
case '1':
return 1;
... itd
czy w okienku
while(!_clicked) sleep(10);
return (pozycja klikniecia myszki przekonwertowana do inta);
dla reszty programu nie bedzie mialo znaczenia... tak samo dla wyswietlenia... czyli ulamek kodu

a bedziesz mial zrobione: sprawdzanie wygranej, przelaczanie graczy, sprawdzanie poprawnosci ruchu, polaczenie miedzy tymi modulami itd.
xxSlayeRxx
Member
 
Posty: 661
Dołączył(a): Pt lip 08, 2011 10:24 pm

Re: Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowo

Postprzez XardasLord » So lis 02, 2013 10:47 pm

Po uzyskanych wskazówkach od różnych forumowiczów, a także od Ciebie Dariuszu przystąpiłem dziś do zaprojektowania modelu gry od początku. Rozbijając ten projekt na coraz mniejsze części pierwsze (klasy) stworzyło mi to straszny problem, aby wszystko powiązać ze sobą obiektowo. A mianowicie:
- jak z klasy A odwołać się do prywatnego pola w klasie B, nie tworząc przy tym obiektów, jak i również nie dziedziczyć klas, aby to uzyskać. Gdyż każda klasa musi pobierać jakąś wartość prywatną z innej klasy i najprościej w świecie mówiąc wykrzaczam się ;/
XardasLord
Member
 
Posty: 14
Dołączył(a): Pt lis 01, 2013 1:48 pm

Re: Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowo

Postprzez DariuszR » N lis 03, 2013 6:39 am

Jak chcesz się odwoływać do pól prywatnych to albo przez właściwości (tak chyba najlepiej), albo przez gettety i settety (metody).

1. Przez właściwości

Kod: Zaznacz cały

class MyClass
{
    private string name;

    public MyClass(string name)
    {
        this.name = name;
    }

   //właściwość RW
    public string Name
    {
        get {return this.name;}
        set {this.name = value;}
    }

}



Teraz przez gettery i settery

Kod: Zaznacz cały

class MyClass
{
    private string name;

    public MyClass(string name)
    {
        this.name = name;
    }

    public string GetName()
    {
        return this.name;
    }

    public void SetName(string value)
    {
        this.name = value;
    }
}



Ten pierwszy jest o wiele bardziej przyjazny ale można się ze mną nie zgodzić. Generalnie unikał bym zmiany statusu pól z prywatnych na publiczne jeżeli nie ma takiego powodu. Chcąc stworzyć właściwość tylko read only pomijasz w pierwszym przykładzie set {this.name = value;) a ma być tylko to co w get. Możesz tworzyć właściwości read-only do wewnętrznych obiektów (np. klasa gracza) i przez nie odwoływać się do ich metod lub właściwości.

Zastosowałem tego typu zabiegi w projekcie neural libs w wątku niżej, konkretniej odwołanie przez właściwości do celów treningu takiej sieci neuronowej MLP. Więc możesz się z tym zapoznać.

Nie powinieneś bać się większej ilości mniejszych klas, po prostu postępujesz tak, że najpierw tworzysz coś z mniejszych obiektów, potem większe z tych już stworzonych (to jak budujesz dom z cegieł albo składasz komp z części a te są obiektami).
DariuszR
Member
 
Posty: 43
Dołączył(a): So paź 05, 2013 3:38 pm

Re: Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowo

Postprzez xxSlayeRxx » N lis 03, 2013 1:52 pm

Jak chcesz sie odwolywac do prywatnych zmiennych to cos zes s...kopal :)
xxSlayeRxx
Member
 
Posty: 661
Dołączył(a): Pt lip 08, 2011 10:24 pm

Re: Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowo

Postprzez XardasLord » Pn lis 04, 2013 12:01 am

Postanowiłem napisać gierkę od nowa, trzymając się porad, które mi udzieliliście.
Byłbym bardzo wdzięczny, gdybyście mogli spojrzeć na mój nowy, poprawiony kod i sprawdzić czy jest napisany lepiej pod względem obiektowym niż poprzedni.

W załączniku przesyłam projekt.

Pozdrawiam :)
Załączniki
Kolko i krzyzyk ver. 1.2.zip
(62.89 KiB) Pobrane 262 razy
XardasLord
Member
 
Posty: 14
Dołączył(a): Pt lis 01, 2013 1:48 pm

Re: Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowo

Postprzez DariuszR » Pn lis 04, 2013 9:01 am

OK, po przejrzeniu kodu jeszcze kilka uwag (ale generalnie lepiej :) ):

1. Nazwy właściwości zaczynające się od get czyli np:

Kod: Zaznacz cały
public override string getName
        {
            get
            {
                return this.name;
            }
        }     


Może czasem wprowadzić błąd albo powodować problemy jeśli chodzi o zrozumienie kodu (a chodzi mi tu o pracę w zespole gdzie pewnie przyjdzie Tobie pracować). Od Get i Set to zaczynają się metody do pobierania lub ustawiania prywatnych pól. Poza tym w C# Get czy Set zaczyna się z dużej litery (nie jak w JAVA z małej) i tu mogą być nieporozumienia.

a więc lepiej:

Kod: Zaznacz cały
public override string Name
        {
            get
            {
                return this.name;
            }
        }     


i Name z dużej litery (zobacz np. w tablicy string[] s jak taką zadeklarujesz to będziesz miał s.Length jako długość tej tablicy.

2. Widzę jakąś niezgodność z SRP (SOLID) w klasie Game. Chodzi o metodę DrawMap(Map map), tzn. można by jeszcze zaimplementować osobną klasę: Presenter, która w konstruktorze pobiera Map map z jakąś metodą Display() i użyć tego w tej metodzie. Przy takim podejściu wydaje mi się prostsza rozbudowa, jako że jeśli chcesz dodać bajery (np. kolory) to robisz to tylko w klasie Presenter.

3. Zastanówmy się jeszcze nad metodą Play() klasy Game.Dałeś: Object x, skoro x jest albo HumanPlayer albo ComputerPlayer a te dziedziczą z Player to równie dobrze może być Player x a jeszcze lepiej Player player bo x to też nie wiadomo co oznacza.

Można jeszcze zastanowić się nad uproszczeniem kodu w metodzie Play bo widzę że się chyba niepotrzebnie powtarzasz. Czyli można by zastanowić się nad jakimś wywołaniem pewnych metod wspólnych dla obu przypadków (HumanPlayer, ComputerPlayer).
DariuszR
Member
 
Posty: 43
Dołączył(a): So paź 05, 2013 3:38 pm

Re: Kółko i krzyżyk - wasze opinie/uwagi pod kątem obiektowo

Sponsors

Sponsor
 

Następna strona

Powrót do Projekty i kody źródłowe

Kto przegląda forum

Użytkownicy przeglądający ten dział: Google [Bot] i 3 gości