Vou oferecer algumas sugestões. Alguns deles se contradizem. Mas talvez alguns sejam úteis.
Considere listas versus sinalizadores
Você pode percorrer o mundo e verificar uma bandeira em cada item para decidir se deve fazer a bandeira. Ou você pode manter uma lista apenas dos itens que devem fazer o sinalizador.
Considere listas e enumerações
Você pode continuar adicionando campos booleanos à sua classe de item, isAThis e isAThat. Ou você pode ter uma lista de strings ou elementos de enumeração, como {"isAThis", "isAThat"} ou {IS_A_THIS, IS_A_THAT}. Dessa forma, você pode adicionar novos na enumeração (ou string consts) sem adicionar campos. Não que haja algo realmente errado em adicionar campos ...
Considere ponteiros de função
Em vez de uma lista de sinalizadores ou enumerações, pode haver uma lista de ações a serem executadas para esse item em diferentes contextos. (Entidade-ish…)
Considere objetos
Algumas pessoas preferem abordagens de entidade orientada a dados, com script ou componente. Mas também vale a pena considerar as hierarquias de objetos à moda antiga. A classe base precisa aceitar as ações, como "jogar esta carta na fase B do turno" ou o que for. Cada tipo de cartão pode substituir e responder conforme apropriado. Provavelmente também existe um objeto de jogador e um objeto de jogo, para que o jogo possa fazer coisas como, se (player-> isAllowedToPlay ()) {faça a jogada ...}.
Considere a capacidade de depuração
Uma coisa interessante sobre uma pilha de campos de bandeira é que você pode examinar e imprimir o estado de cada item da mesma maneira. Se o estado é representado por diferentes tipos, bolsas de componentes ou ponteiros de função, ou estar em listas diferentes, pode não ser suficiente apenas olhar para os campos do item. São todas as trocas.
Eventualmente, refatoração: considere testes de unidade
Não importa o quanto você generalize sua arquitetura, poderá imaginar coisas que ela não cobre. Então você terá que refatorar. Talvez um pouco, talvez muito.
Uma maneira de tornar isso mais seguro é com um corpo de testes de unidade. Dessa forma, você pode ter certeza de que, mesmo reorganizando as coisas por baixo (talvez muito!), A funcionalidade existente ainda funciona. Cada teste de unidade é, geralmente, assim:
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
}
Como você pode ver, manter essas chamadas de API de nível superior no jogo (ou jogador, cartão etc.) é essencial para a estratégia de teste de unidade.