Offrirò una manciata di suggerimenti. Alcuni si contraddicono a vicenda. Ma forse alcuni sono utili.
Considera gli elenchi rispetto ai flag
Puoi iterare in tutto il mondo e controllare una bandiera su ogni oggetto per decidere se fare la cosa bandiera. Oppure puoi tenere un elenco solo di quegli elementi che dovrebbero fare la cosa bandiera.
Considera elenchi ed enumerazioni
Puoi continuare ad aggiungere campi booleani alla tua classe di articoli, isAThis e isAThat. Oppure puoi avere un elenco di stringhe o elementi enum, come {"isAThis", "isAThat"} o {IS_A_THIS, IS_A_THAT}. In questo modo è possibile aggiungerne di nuovi all'enumerazione (o contro stringhe) senza aggiungere campi. Non che ci sia qualcosa di veramente sbagliato nell'aggiungere campi ...
Considera i puntatori a funzioni
Invece di un elenco di flag o enumerazioni, potrebbe avere un elenco di azioni da eseguire per quell'elemento in contesti diversi. (Entity-ish ...)
Considera gli oggetti
Alcune persone preferiscono approcci basati sui dati, basati su script o su entità componenti. Ma vale la pena considerare anche le gerarchie di oggetti vecchio stile. La classe base deve accettare le azioni, come "gioca questa carta per la fase di turno B" o qualsiasi altra cosa. Quindi ogni tipo di carta può sovrascrivere e rispondere come appropriato. Probabilmente c'è anche un oggetto giocatore e un oggetto di gioco, quindi il gioco può fare cose come, se (player-> isAllowedToPlay ()) {fare il gioco ...}.
Prendi in considerazione l'abilità di debug
Una cosa bella di una pila di campi bandiera è che puoi esaminare e stampare lo stato di ogni oggetto allo stesso modo. Se lo stato è rappresentato da tipi diversi, o gruppi di componenti, o puntatori a funzioni, o essendo in elenchi diversi, potrebbe non essere sufficiente guardare solo i campi dell'elemento. Sono tutti compromessi.
Alla fine, refactoring: considerare i test unitari
Non importa quanto generalizzi la tua architettura, sarai in grado di immaginare cose che non riguardano. Quindi dovrai refactoring. Forse un po ', forse molto.
Un modo per rendere questo più sicuro è con un insieme di test unitari. In questo modo puoi essere sicuro che anche se hai riorganizzato le cose sottostanti (forse di molto!) La funzionalità esistente funziona ancora. Ogni test unitario si presenta, in genere, come questo:
void test1()
{
Game game;
game.addThis();
game.setupThat(); // use primary or backdoor API to get game to known state
game.playCard(something something).
int x = game.getSomeInternalState;
assertEquals(“did it do what we wanted?”, x, 23); // fail if x isn’t 23
}
Come puoi vedere, mantenere stabili quelle chiamate API di alto livello sul gioco (o giocatore, carta, ecc.) È la chiave della strategia di test delle unità.