Je proposerai quelques suggestions. Certains d'entre eux se contredisent. Mais peut-être que certains sont utiles.
Considérez les listes par rapport aux indicateurs
Vous pouvez parcourir le monde et vérifier un drapeau sur chaque élément pour décider de faire le drapeau. Ou vous pouvez garder une liste des seuls éléments qui devraient faire le drapeau.
Tenez compte des listes et des énumérations
Vous pouvez continuer à ajouter des champs booléens à votre classe d'élément, isAThis et isAThat. Ou vous pouvez avoir une liste de chaînes ou d'éléments d'énumération, comme {"isAThis", "isAThat"} ou {IS_A_THIS, IS_A_THAT}. De cette façon, vous pouvez en ajouter de nouveaux dans l'énumération (ou les chaînes de caractères) sans ajouter de champs. Pas qu'il y ait vraiment quelque chose de mal à ajouter des champs ...
Considérez les pointeurs de fonction
Au lieu d'une liste d'indicateurs ou d'énumérations, pourrait avoir une liste d'actions à exécuter pour cet élément dans différents contextes. (Entité-ish…)
Considérez les objets
Certaines personnes préfèrent les approches basées sur les données, les scripts ou les entités de composants. Mais les hiérarchies d'objets à l'ancienne méritent également d'être prises en compte. La classe de base doit accepter les actions, comme «jouer cette carte pour la phase de tour B» ou autre chose. Ensuite, chaque type de carte peut remplacer et répondre comme il convient. Il y a probablement aussi un objet joueur et un objet jeu, donc le jeu peut faire des choses comme, si (player-> isAllowedToPlay ()) {joue le jeu…}.
Envisagez la capacité de débogage
Une fois une bonne chose à propos d'une pile de champs de drapeau, c'est que vous pouvez examiner et imprimer l'état de chaque élément de la même manière. Si l'état est représenté par différents types, ou des sacs de composants, ou des pointeurs de fonction, ou étant dans des listes différentes, il ne suffit peut-être pas de simplement regarder les champs de l'élément. Ce sont tous des compromis.
Finalement, refactoring: envisager des tests unitaires
Peu importe combien vous généralisez votre architecture, vous pourrez imaginer des choses qu'elle ne couvre pas. Ensuite, vous devrez refactoriser. Peut-être un peu, peut-être beaucoup.
Un moyen de rendre cela plus sûr est avec un ensemble de tests unitaires. De cette façon, vous pouvez être sûr que même si vous avez réorganisé les choses en dessous (peut-être beaucoup!), La fonctionnalité existante fonctionne toujours. Chaque test unitaire ressemble généralement à ceci:
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
}
Comme vous pouvez le voir, la stabilité de ces appels d'API de niveau supérieur sur le jeu (ou le joueur, la carte, etc.) est la clé de la stratégie de test unitaire.