2010-07-02 12 views
5

Wdrażam prostą grę "Zagraj w swoje karty w prawo" (zwaną inaczej wyższą/niższą). Jeśli nie natkniesz się na to, zanim zasady będą naprawdę proste. Wykorzystywany jest jeden zestaw kart (na przykład kier). Jedna karta jest losowana na raz, a celem jest prawidłowe odgadnięcie, czy wartość nominalna następnej karty będzie wyższa lub niższa od wartości nominalnej poprzednio pobranej karty.Java - Porady dotyczące projektowania prostych gier

Logika gry nie jest szczególnie skomplikowana, nie martwię się o to. Wymyśliłem projekt, ale nie jestem z niego całkowicie zadowolony. Jest kilka obszarów, w których jestem pewien, że można je ulepszyć, i to jest to, co chciałbym uzyskać od twojej porady. Oto interfejs dla klasy (komentarze do dodatkowego porozumienia, a nie prawdziwe komentarze):

public interface PlayYourCardsRight { 

/** 
* Get the number of cards remaining with higher face values than the previously 
* drawn card 
* @return 
*/ 

public abstract int getNumberCardsHigher(); 

/** 
* Get the number of cards remaining with lower face values than the previously 
* drawn card 
* @return 
*/ 

public abstract int getNumberCardsLower(); 

/** 
* Get all cards that have already been drawn in the order they were drawn in 
* 
*/ 

public abstract List<Card> getPlayedCards(); 

/** 
* Simple prediction algorithm - if there are more cards left in the deck with 
* lower face values than the previous card, then predict 'Lower', if there 
* are more cards left in the deck with higher face values then predict 
* 'Higher', if there are equal numbers of higher/lower cards pick 'higher' or  'lower' 
* at random 
* 
* Prediction is an Enum (Higher/Lower/None) 
* 
*/ 

public abstract Prediction getPrediction(); 

/* 
* Draw the next card at random 
*/ 

public abstract void nextRound(); 

/** 
* Specifiy what the next card should be 
* 
* @param card 
*/ 

public abstract void nextRound(Card card); 

} 

Jak widać to wszystko jest dość oczywista i prosta. Oto moje problemy:

Nie chcę, aby konstruktor automatycznie narysował kartę. Oznacza to, że początkowo nie ma "poprzednio narysowanej karty". Mam wartość NO PREDICTION w enum Prediction, ale ponieważ nie ma "wcześniej narysowanej karty", metody nie mogą zwracać prawidłowych wartości (nie mogą również zwracać normalnych wartości, gdy wszystkie karty z talii zostały narysowane).

Oczywiście mógłbym po prostu wyrzucić wyjątek, ale to wydaje się przesadą - zwłaszcza, że ​​wtedy wszystkie wywołania metod muszą być zapakowane w try/catches. Jestem również niezadowolony z tego, że zwracam wartość ujemną, ponieważ może to łatwo doprowadzić do błędów, jeśli ktoś zapomni/nie będzie mógł się tym zająć.

Wszystkie sugestie są mile widziane!

Odpowiedz

4

Osobiście nie myśl, że wyrzucenie niesprawdzonego wyjątku w przypadku sprawdzania argumentów jest w ogóle przesadzone - zakłada się, że twój kod potwierdza stan nieprawidłowy (nie powinieneś wywoływać tych metod z obiektem w tym stanie, EVER).

Zazwyczaj używam wyjątku IllegalArgumentException, aby pokazać, że argument został przekazany, który nie pasuje do umowy wywołania metody, oraz IllegalStateException, aby pokazać, że obiekt nie jest w stanie obsługi wywołania metody w tym czasie .

Ponieważ oba są niezaznaczonymi wyjątkami, nie musisz próbować/złapać ich, po prostu pozwól im się odświeżyć, robią to, w czym wyjątki są świetne - dają ci ślad stosu i mówią ci dokładnie, gdzie jest twój błąd jest tym, który nazwał go niepoprawnie.

Zwykle używam jakiegoś napisu na drodze, w Twoim przypadku to może być:

throw new IllegalStateException("You cannot call this method until a card has been drawn"); 

Logicznie to po prostu nie ma sensu pytać, czy karta jest wyższa lub niższa niż karta, która nie istnieje.

Teraz, jeśli twoja metoda rzeczywiście PRZEWAJE ten wyjątek, musisz poprawić kod tak, aby nie wywoływał tej metody, dopóki nie narysuje karty - więc musisz wymyślić, jak narysować twoja pierwsza karta niezależnie.

Uwaga: Wyjątki służą wyłącznie do wykrywania błędów, należy unikać ich używania do kontroli przepływu. Oznacza to, że nie powinieneś próbować wychwycić wyjątku i użyć go do narysowania karty, a następnie zadzwonić ponownie! Zamiast tego należy programować w taki sposób, aby zagwarantować, że karta zostanie narysowana przed pierwszym wywołaniem metod.

0

Twierdzę, że obie metody powinny zwrócić card.count, gdy nie ma poprzedniej karty, która została narysowana. Została ta sama liczba niższych i wyższych kart, a dla obu kart liczy się więcej wyższych/niższych kart niż nic. Twój algorytm będzie działał i zwróci wartość NO_PREDICTION.

+0

Nie wydaje mi się, aby obie metody zwracały "card.count" w tym przypadku. To rozwiązanie sugerowałoby, że jest 13 kart wyższych i 13 kart niższe, co oznacza, że ​​26 kart jest ogólnie dostępnych, mimo że w grze jest tylko 13 kart w grze. Chociaż wiemy, co się dzieje, wyobraź sobie prezentowanie tej informacji użytkownikowi. Wydaje się to być bardzo niesatysfakcjonującym zachowaniem. – Peter

+0

@Peter: Moje rozumowanie jest następujące: karta jest zarówno niższa, jak i wyższa niż brak karty. Jest to jedna z tych sytuacji, w których dwa przeciwstawne stany są równe w odniesieniu do pojęcia nicości. Nie wydaje mi się to sprzeczne z intuicją, ale to tylko moja opinia. Gdy nie ma już kart, obie powinny zwrócić 0, więc na tym etapie będą równe. Na początku oba powinny zwracać przeciwne 0, w tym przypadku card.count. – JRL

0

Osobiście polecam posiadanie karty początkowej, zanim gracz zrobi cokolwiek, ponieważ nie ma sensu, aby gracz zrobił cokolwiek przed pierwszą kartą, ale myślę: "Nie chcę, aby konstruktor automatycznie narysuj kartę "oznacza, że ​​nie chcesz tego robić. Jeśli nie chcesz tego zrobić, sprawiłbym, że funkcje rzucają wyjątki i mają kod, który je wywołuje (funkcja przewidywania), specjalny przypadek, początek gry, aby powrócić "bez przewidywania", zamiast nawet próbować je wywołać. Koniec gry nie jest specjalnym przypadkiem; obie funkcje powinny powrócić 0, jak nie ma karty wyższe lub niższe niż się karty w talii

Ponadto, nie ma potrzeby deklarowania każdą funkcję abstract w interfejsie, to automatycznie i wymaga

+0

Interesujący (i poprawny punkt) na temat zakończenia gry, powinienem był o tym pomyśleć! Jednak szukam alternatywnych rozwiązań korzystania z wyjątków, nawet drastycznie zmieniając mój projekt. – Peter

+0

Dla jasności, Wiem, że nie musisz deklarować metod interfejsu jako abstrakcyjnych, Eclipse zrobił to automatycznie (z jakiegoś powodu) po wyodrębnieniu interfejsu. ** chce edytować komentarze ** – Peter

+0

@Peter Możesz, ale tylko przez pięć minut :) –

1

Nie chcę, aby konstruktor automatycznie rysował kartę. Oznacza to, że początkowo nie ma "poprzednio narysowanej karty". Mam wartość NO PREDICTION w wyliczeniu przewidywania, ale ponieważ nie ma "wcześniej narysowanej karty" metody getNumberCardsHigher() i getNumberCardsLower() nie mogą zwrócić normalnych wartości (nie mogą również zwrócić normalnych wartości, gdy wszystkie karty z talii zostały narysowane).

myślę zamieszanie API wynika z faktu, że interfejs PlayYourCardsRight stara się modelować dwie oddzielne rzeczy: Silnik gry/zasad i talii kart. Przeniosę stan talii kart i metody liczenia pozostałych kart do klasy Deck. Zmieniłbym interfejs API na getNumberCards[Higher/Lower](Card) i pozwoliłbym silnikowi gry określić kartę, z którą chce się porównać, zamiast oczekiwać, że talia będzie pamiętała, która karta została wyciągnięta jako ostatnia, co uważam za element stanu gry, a nie talię.

Bardzo polecam napisanie kilku testów JUnit. TDD pomaga stworzyć spójny, niezwiązany interfejs API.

Powiązane problemy