Ofreceré un puñado de sugerencias. Algunos de ellos se contradicen entre sí. Pero tal vez algunos sean útiles.
Considere listas versus banderas
Puede recorrer el mundo y marcar una bandera en cada elemento para decidir si se debe hacer la bandera. O puede mantener una lista de solo aquellos elementos que deberían hacer la bandera.
Considere listas y enumeraciones
Puede seguir agregando campos booleanos a su clase de elemento, isAThis y isAThat. O puede tener una lista de cadenas o elementos de enumeración, como {"isAThis", "isAThat"} o {IS_A_THIS, IS_A_THAT}. De esa manera, puede agregar nuevos en la enumeración (o consts de cadena) sin agregar campos. No es que haya nada realmente malo al agregar campos ...
Considerar punteros de función
En lugar de una lista de indicadores o enumeraciones, podría tener una lista de acciones para ejecutar ese elemento en diferentes contextos. (Entidad-ish ...)
Considerar objetos
Algunas personas prefieren enfoques basados en datos, guiones o entidades componentes. Pero también vale la pena considerar las jerarquías de objetos anticuados. La clase base necesita aceptar las acciones, como "jugar esta carta para la fase de turno B" o lo que sea. Luego, cada tipo de tarjeta puede anular y responder según corresponda. Probablemente también haya un objeto de jugador y un objeto de juego, por lo que el juego puede hacer cosas como, si (player-> isAllowedToPlay ()) {hace el juego ...}.
Considere la capacidad de depuración
Una vez que lo bueno de una pila de campos de banderas es que puede examinar e imprimir el estado de cada elemento de la misma manera. Si el estado está representado por diferentes tipos, o bolsas de componentes, o punteros de función, o está en diferentes listas, puede que no sea suficiente solo mirar los campos del elemento. Todo son compensaciones.
Eventualmente, refactorización: considere las pruebas unitarias
No importa cuánto generalice su arquitectura, podrá imaginar cosas que no cubre. Entonces tendrás que refactorizar. Quizás un poco, quizás mucho.
Una forma de hacer esto más seguro es con un cuerpo de pruebas unitarias. De esa manera, puede estar seguro de que, aunque haya reorganizado las cosas debajo (¡tal vez por mucho!), La funcionalidad existente aún funciona. Cada prueba unitaria generalmente se ve así:
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 puede ver, mantener estables las llamadas de API de nivel superior en el juego (o jugador, tarjeta, etc.) es clave para la estrategia de prueba de la unidad.