2010-07-02 13 views
5

Sto implementando un semplice gioco "Gioca a carte a destra" (altrimenti noto come superiore/inferiore). Nel caso in cui non l'abbia incontrato prima che le regole siano davvero semplici. Viene utilizzato un singolo seme di carte (ad esempio cuori). Una carta viene pescata alla volta e l'obiettivo è quello di indovinare correttamente se il valore nominale della carta successiva sarà superiore o inferiore al valore nominale della carta precedentemente pescata.Java - Consigli di progettazione per il gioco semplice

La logica del gioco non è particolarmente complessa, non ne sono preoccupato. Ho ideato un design, ma non ne sono completamente soddisfatto. Ci sono alcune aree in cui sono sicuro che potrebbe essere migliorato, ed è quello che vorrei ricevere il tuo consiglio. Ecco un'interfaccia per la classe (commenti per la comprensione ulteriore, non reale i commenti):

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); 

} 

Come si può vedere è tutto piuttosto intuitivo e semplice. Ecco i miei problemi:

Non voglio che il costruttore disegni automaticamente una carta. Ciò significa che inizialmente non esiste una "carta precedentemente disegnata". Ho un valore Prediction nell'enumerazione Prediction ma, poiché non esiste una "carta disegnata in precedenza", i metodi getNumberCardsHigher() e getNumberCardsLower() non possono restituire valori sensati (non possono anche restituire valori sani quando sono state disegnate tutte le carte del mazzo).

Ovviamente, potrei semplicemente lanciare un'eccezione, ma ciò sembra eccessivo, soprattutto perché tutte le chiamate ai metodi devono essere racchiuse in try/catches. Sono anche scontento di restituire un valore negativo, poiché ciò potrebbe facilmente portare ad alcuni errori se qualcuno dimentica/non può essere disturbato a controllarli.

Tutti i suggerimenti sono benvenuti!

risposta

4

Personalmente non pensare di lanciare un'eccezione non controllata nel caso in cui il controllo degli argomenti sia eccessivo - si presume che il tuo codice stia asserendo uno stato non valido (non dovresti chiamare quei metodi con l'oggetto in quello stato, MAI).

Di solito uso un IllegalArgumentException per mostrare che è stato passato un argomento che non si adatta al contratto della chiamata al metodo e un IllegalStateException per mostrare che l'oggetto non è nello stato per gestire la chiamata al metodo in questo momento .

Dal momento che sono entrambe eccezioni non controllate, non devi cercare/catturarle, lasciarle solo saltare in aria, fanno ciò che le eccezioni sono grandi - ti danno una traccia dello stack e ti dicono esattamente dove si trova il tuo bug sta includendo chi ha chiamato in modo errato.

Io di solito uso un qualche tipo di corda tra l'altro, nel tuo caso potrebbe essere:

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

Logicamente semplicemente non ha senso chiedere se la carta è superiore o inferiore a una carta che non esiste

Ora, se il tuo metodo effettivamente LANCIA quell'eccezione, allora devi andare avanti e correggere il tuo codice in modo che non chiami quel metodo fino a quando non ha disegnato una carta - quindi devi capire come disegnare la tua prima carta indipendentemente.

Nota: le eccezioni sono solo per il rilevamento degli errori, evitare di utilizzarle per il controllo del flusso. Ciò significa che non dovresti cercare di catturare l'eccezione e usarla per disegnare una carta, quindi chiamare di nuovo! Invece dovresti programmare in modo tale da garantire che una carta venga disegnata prima che vengano chiamati i metodi.

0

Direi che entrambi i metodi dovrebbero restituire card.count quando non c'è alcuna carta precedente che è stata disegnata. C'è lo stesso numero di carte inferiori e superiori rimaste, e per entrambi c'è il conteggio delle carte più alto/più basso del nulla. Il tuo algoritmo funzionerebbe e restituirebbe un NO_PREDICTION.

+0

Non penso abbia senso per entrambi i metodi restituire 'card.count' in questo caso. Questa soluzione suggerirebbe che ci sono 13 carte più in alto e 13 carte in meno, il che implica 26 carte in totale, anche se ci sono solo 13 carte in un seme e nel gioco. Anche se sappiamo cosa succede immaginiamo di presentare queste informazioni a un utente. Sembra un comportamento profondamente insoddisfacente. – Peter

+0

@Peter: il mio ragionamento è il seguente: una carta è sia inferiore sia superiore all'assenza di una carta. Questa è una di quelle situazioni in cui due stati opposti sono uguali rispetto al concetto di nulla. Non mi sembra affatto intuitivo, ma è solo la mia opinione. Quando non ci sono più carte, entrambi dovrebbero restituire 0, quindi anche in quel momento sarebbero uguali. All'inizio, dovrebbero quindi restituire entrambi il contrario di 0, in questo caso card.count. – JRL

0

Io personalmente suggerirei di avere una carta iniziale prima che il giocatore faccia qualcosa, in quanto non ha senso fare fare qualcosa prima che la prima carta sia alzata, ma penso "Non voglio che il costruttore disegnare automaticamente una carta "significava che non volevi farlo. Se non si desidera farlo, farei in modo che le funzioni generino eccezioni e il codice che chiama il caso speciale (la funzione di previsione) l'inizio del gioco restituisca "nessuna previsione" invece di provare a chiamarle. La fine del gioco non è un caso speciale; entrambe le funzioni devono restituiscono 0, in quanto non vi sono carte superiore o inferiore della carta fino rimanente nel mazzo

Inoltre, non c'è bisogno di dichiarare ogni funzione abstract in un'interfaccia, è automatico e richiedeva

+0

Interessante (e punto corretto) circa la fine del gioco, avrei dovuto pensarci! Tuttavia, sto cercando soluzioni alternative all'utilizzo di Exceptions, cambiando drasticamente anche il mio design. – Peter

+0

Giusto per essere chiari, so che non è necessario dichiarare i metodi di interfaccia come astratti, Eclipse l'ha fatto automaticamente (per qualche ragione) quando ho estratto l'interfaccia. ** desidera che tu possa modificare i commenti ** – Peter

+0

@Peter Puoi, ma solo per cinque minuti :) –

1

Non voglio che il costruttore disegni automaticamente una scheda. Ciò significa che inizialmente non esiste una "carta precedentemente disegnata". Ho un valore di NON PREVENZIONE nell'enunciazione della predizione ma, poiché non esiste una "carta precedentemente disegnata", i metodi getNumberCardsHigher() e getNumberCardsLower() non possono restituire i valori sani (non possono anche restituire i valori sani quando tutte le carte dal mazzo sono stati disegnati).

penso che la confusione API nasce dal fatto che l'interfaccia PlayYourCardsRight sta cercando di modellare due cose separate: il motore di gioco/Regole e il mazzo di carte. Sposterei lo stato del mazzo di carte e i metodi di conteggio delle carte rimanenti in una classe Deck. Vorrei cambiare l'API in getNumberCards[Higher/Lower](Card) e lasciare che il motore di gioco specifichi quale carta vuoi confrontare, invece di aspettarti che il mazzo ricordi quale carta è stata pescata per ultima, che vedrei come elemento dello stato del gioco, non del mazzo.

E consiglio vivamente di scrivere alcuni test JUnit. TDD aiuta a produrre un'API coesiva e disaccoppiata.

Problemi correlati