Combiner la méthode du modèle avec la stratégie

14

Une tâche dans ma classe de génie logiciel est de concevoir une application qui peut jouer différentes formes d'un jeu particulier. Le jeu en question est Mancala, certains de ces jeux sont appelés Wari ou Kalah. Ces jeux diffèrent sur certains aspects, mais pour ma question, il est seulement important de savoir que les jeux peuvent différer de la manière suivante:

  • La façon dont le résultat d'un déménagement est géré
  • La façon dont la fin de la partie est déterminée
  • La façon dont le gagnant est déterminé

La première chose qui m'est venue à l'esprit pour concevoir ceci était d'utiliser le modèle de stratégie, j'ai une variation dans les algorithmes (les règles réelles du jeu). Le design pourrait ressembler à ceci: entrez la description de l'image ici

Je me suis alors dit que dans le jeu de Mancala et Wari, la façon dont le gagnant est déterminé est exactement la même et le code serait dupliqué. Je ne pense pas que ce soit par définition une violation du principe `` une règle, un seul endroit '' ou DRY, car un changement dans les règles pour Mancala ne signifierait pas automatiquement que la règle devrait également être modifiée dans Wari. Néanmoins, grâce aux commentaires de mon professeur, j'ai eu l'impression de trouver un design différent.

J'ai ensuite trouvé ceci: entrez la description de l'image ici

Chaque jeu (Mancala, Wari, Kalah, ...) aurait juste un attribut du type d'interface de chaque règle, c'est WinnerDeterminer-à- dire et s'il y a une version Mancala 2.0 qui est la même que Mancala 1.0 sauf pour la façon dont le gagnant est déterminé, il peut simplement utilisez les versions de Mancala.

Je pense que la mise en œuvre de ces règles en tant que modèle de stratégie est certainement valable. Mais le vrai problème vient quand je veux le concevoir davantage.

En lisant sur le modèle de méthode de modèle, j'ai immédiatement pensé qu'il pouvait être appliqué à ce problème. Les actions effectuées lorsqu'un utilisateur effectue un mouvement sont toujours les mêmes et dans le même ordre, à savoir:

  • déposer des pierres dans des trous (c'est le même pour tous les jeux, donc serait implémenté dans la méthode du modèle elle-même)
  • déterminer le résultat du déménagement
  • déterminer si le jeu est terminé à cause du coup précédent
  • si le jeu est terminé, déterminez qui a gagné

Ces trois dernières étapes sont toutes dans mon modèle de stratégie décrit ci-dessus. J'ai beaucoup de mal à combiner ces deux. Une solution possible que j'ai trouvée serait d'abandonner le modèle de stratégie et de faire ce qui suit: entrez la description de l'image ici

Je ne vois pas vraiment la différence de conception entre le modèle de stratégie et celui-ci? Mais je suis certain que je dois utiliser une méthode de modèle (bien que j'étais tout aussi sûr d'avoir à utiliser un modèle de stratégie).

Je ne peux pas non plus déterminer qui serait responsable de la création de l' TurnTemplateobjet, alors qu'avec le modèle de stratégie, j'ai le sentiment d'avoir des familles d'objets (les trois règles) que je pourrais facilement créer en utilisant un modèle d'usine abstrait. Je puis un MancalaRuleFactory, WariRuleFactory, etc. , et ils créeraient les instances correcte des règles et la main - moi un RuleSetobjet.

Disons que j'utilise le modèle de stratégie + usine abstraite et que j'ai un RuleSetobjet qui contient des algorithmes pour les trois règles. La seule façon dont je sens que je peux encore utiliser le modèle de méthode de modèle avec ceci est de passer cet RuleSetobjet à mon TurnTemplate. Le «problème» qui fait alors surface est que je n'aurais jamais besoin de mes implémentations concrètes de la TurnTemplate, ces classes deviendraient obsolètes. Dans mes méthodes protégées dans le TurnTemplateje pourrais simplement appeler ruleSet.determineWinner(). En conséquence, la TurnTemplateclasse ne serait plus abstraite mais devrait devenir concrète, s'agit-il alors encore d'un modèle de méthode modèle?

Pour résumer, est-ce que je pense dans le bon sens ou est-ce que je manque quelque chose de facile? Si je suis sur la bonne voie, comment combiner un modèle de stratégie et un modèle de méthode de modèle?

Mekswoll
la source
1
Je dirais que votre réflexion est à peu près exacte. Si les commentaires que vous obtenez de votre professeur ne valident pas cela, demandez-lui de signaler les défauts ou donnez un indice de ce qu'il recherche dans la réponse. Arrêtez d'essayer de lire dans les esprits. En supposant que ce que votre professeur (et plus tard vos utilisateurs) veulent, c'est peut-être le pire que vous puissiez faire.
Marjan Venema
" ... dans le jeu de Mancala et Wari, la façon dont le gagnant est déterminé est exactement la même et le code serait dupliqué. " Pourquoi cela implique-t-il que le code est dupliqué? Laissez simplement les deux classes appeler la même fonction.
D Drmmr

Réponses:

6

Après avoir examiné vos conceptions, vos première et troisième itérations semblent être des conceptions plus élégantes. Cependant, vous mentionnez que vous êtes étudiant et votre professeur vous a fait part de vos commentaires. Sans savoir exactement quel est votre devoir ou le but de la classe ou plus d'informations sur ce que votre professeur a suggéré, je prendrais tout ce que je dis ci-dessous avec un grain de sel.

Dans votre premier design, vous déclarez RuleInterfaceêtre une interface qui définit comment gérer le tour de chaque joueur, comment déterminer si le jeu est terminé et comment déterminer un gagnant après la fin du jeu. Il semble que ce soit une interface valide pour une famille de jeux qui connaissent des variations. Cependant, selon les jeux, vous pourriez avoir du code en double. Je suis d'accord que la flexibilité de changer les règles d'un jeu est une bonne chose, mais je dirais également que la duplication de code est terrible pour les défauts. Si vous copiez / collez du code défectueux entre les implémentations et que l'un contient un bogue, vous avez maintenant plusieurs bogues qui doivent être corrigés à différents emplacements. Si vous réécrivez les implémentations à différents moments, vous pouvez introduire des défauts à différents emplacements. Aucun de ces éléments n'est souhaitable.

Votre deuxième design semble assez complexe, avec un arbre d'héritage profond. Au moins, c'est plus profond que ce à quoi je m'attendais pour résoudre ce type de problème. Vous commencez également à diviser les détails de l'implémentation en d'autres classes. En fin de compte, vous modélisez et implémentez un jeu. Cela pourrait être une approche intéressante si vous deviez mélanger et assortir vos règles pour déterminer les résultats d'un coup, la fin de la partie et un gagnant, ce qui ne semble pas correspondre aux exigences que vous avez mentionnées. . Vos jeux sont des ensembles de règles bien définis, et j'essaierais d'encapsuler les jeux autant que possible dans des entités distinctes.

Votre troisième design est celui que j'aime le plus. Ma seule préoccupation est que ce n'est pas au bon niveau d'abstraction. En ce moment, vous semblez modéliser un virage. Je recommanderais d'envisager de concevoir le jeu. Considérez que vous avez des joueurs qui font des mouvements sur une sorte de plateau en utilisant des pierres. Votre jeu nécessite la présence de ces acteurs. À partir de là, votre algorithme n'est pas doTurn()mais playGame(), qui va du coup initial au coup final, après quoi il se termine. Après le coup de chaque joueur, il ajuste l'état du jeu, détermine si le jeu est dans un état final, et si c'est le cas, détermine le gagnant.

Je recommanderais de regarder de plus près vos premier et troisième designs et de travailler avec eux. Cela pourrait aussi aider à penser en termes de prototypes. À quoi ressembleraient les clients qui utilisent ces interfaces? Une approche de conception a-t-elle plus de sens pour implémenter un client qui va réellement instancier un jeu et y jouer? Vous devez comprendre avec quoi il interagit. Dans votre cas particulier, c'est la Gameclasse et tout autre élément associé - vous ne pouvez pas concevoir de manière isolée.


Puisque vous mentionnez que vous êtes un étudiant, je voudrais partager quelques éléments d'une époque où j'étais l'AT pour un cours de conception de logiciels:

  • Les motifs sont simplement un moyen de capturer des choses qui ont fonctionné dans le passé, mais de les résumer à un point où ils peuvent être utilisés dans d'autres conceptions. Chaque catalogue de modèles de conception donne un nom à un modèle, explique ses intentions et où il peut être utilisé, et les situations où cela contraindrait finalement votre conception.
  • Le design vient avec l'expérience. La meilleure façon de devenir bon en conception n'est pas simplement de se concentrer sur les aspects de la modélisation, mais de réaliser ce qui entre dans la mise en œuvre de ce modèle. La conception la plus élégante est utile si elle ne peut pas être facilement mise en œuvre ou si elle ne s'intègre pas dans la conception plus large du système ou avec d'autres systèmes.
  • Très peu de modèles sont «corrects» ou «faux». Tant que la conception répond aux exigences du système, cela ne peut pas être faux. Une fois qu'il y a un mappage de chaque exigence dans une certaine représentation de la façon dont le système va répondre à cette exigence, la conception ne peut pas être erronée. À ce stade, il ne s'agit que d'une question qualitative concernant des concepts tels que la flexibilité ou la réutilisabilité ou la testabilité ou la maintenabilité.
Thomas Owens
la source
Merci pour les bons conseils, en particulier votre point de vue sur le fait que le niveau d'abstraction était incorrect. Je l'ai changé maintenant en un GameTemplatequi se sent beaucoup mieux. Cela me permet également de combiner une méthode d'usine pour initialiser les joueurs, le plateau, etc.
Mekswoll
3

Votre confusion est justifiée. Le fait est que les modèles ne s'excluent pas mutuellement.

La méthode de modèle est la base d'un tas d'autres modèles, tels que la stratégie et l'état. Essentiellement, l'interface de stratégie contient une ou plusieurs méthodes de modèle, chacune nécessitant que tous les objets implémentant une stratégie aient (au moins) quelque chose comme une méthode doAction (). Cela permet aux stratégies de se substituer les unes aux autres.

En Java, une interface n'est rien d'autre qu'un ensemble de méthodes de modèle. De même, toute méthode abstraite est essentiellement une méthode modèle. Ce modèle (entre autres) était bien connu des concepteurs de la langue, alors ils l'ont intégré.

@ThomasOwens offre d'excellents conseils pour aborder votre problème particulier.

Matthew Flynn
la source
0

Si vous êtes distrait par les modèles de conception, je vous conseillerais de commencer par créer un prototype du jeu, puis les modèles devraient simplement vous sauter dessus. Je ne pense pas qu'il soit vraiment possible ou conseillé d'essayer de concevoir un système parfaitement en premier, puis de l'implémenter (de la même manière, je trouve cela perplexe lorsque les gens essaient d'abord d'écrire des programmes entiers puis de compiler, plutôt que de le faire petit à petit) .) Le problème est qu'il est peu probable que vous pensiez à chaque scénario auquel votre logique devra faire face, et pendant la phase de mise en œuvre, vous perdrez tout espoir ou essayez de vous en tenir à votre conception défectueuse d'origine et d'introduire des hacks, ou même pire ne livre rien du tout.

James
la source
2
Bien qu'en général je suis d'accord avec vous , ce n'est pas vraiment une réponse qui résout le problème du PO. «Ne fais pas ça» est une réponse, mais plutôt mauvaise si elle ne fournit pas d'alternative viable.
yannis
@YannisRizos Et bien que je sois également d'accord avec vous , je pense toujours qu'il a donné de bons conseils (ou un aperçu si les conseils ne sont pas le bon mot). Pour cette raison, +1. Même si, oui, ce n'est pas techniquement une réponse très utile.
acheté777
-1

Passons aux punaises de laiton. Aucune interface de jeu, aucun modèle de conception, aucune classe abstraite et aucun UML ne sont absolument nécessaires.

Si vous disposez d'un nombre raisonnable de classes de support, telles que l'interface utilisateur, la simulation, etc., pratiquement tout votre code non spécifique à la logique du jeu est de toute façon réutilisé. De plus, votre utilisateur ne change pas son jeu dynamiquement. Vous ne basculez pas à 30 Hz entre les jeux. Vous jouez à un jeu pendant environ une demi-heure. Votre polymorphisme "dynamique" n'est donc pas du tout dynamique. C'est plutôt statique.

Donc, la meilleure façon d'aller ici est d'utiliser une abstraction fonctionnelle générique, comme C # Actionou C ++ std::function, de créer une classe Mancala, une Wari et une Kalah, et de partir de là.

std::unordered_map<std::string, std::function<void()>> games = {
    { "Kalah", [] { return Kalah(); } },
    { "Mancala", [] { return Mancala(); } },
    { "Wari", [] { return Wari(); } }
};
void play() {
    std::function<void()> f;
    f = games[GetChoiceFromUI()];
    f();
    if (PlayAgain()) return play();
}
int main() {
    play();
}

Terminé.

Vous n'appelez pas Games. Les jeux vous appellent.

DeadMG
la source
3
Je ne vois pas du tout comment cela est utile. La personne qui pose cette question est un étudiant. Il pose explicitement des questions sur les principes de conception et les interactions entre deux modèles de conception. Dire qu'il n'y a pas besoin de modèles de conception ou d'UML pourrait être vrai dans certains cas dans l'industrie, mais penser aux modèles de conception, aux modèles de conception et à UML pourrait être le point de départ de l'exercice.
Thomas Owens
3
Il n'y a rien à dire à leur sujet, car il n'y a nulle part où ils s'appliquent. Il est inutile de passer votre temps à réfléchir à la façon dont vous pouvez complètement concevoir le design à l'aide de modèles de conception, à moins que vous ne vouliez que ce soit une leçon sur la façon de ne pas les utiliser.
DeadMG
4
Il y a une tendance inquiétante / ennuyeuse dans SoftDev où les modèles de conception utilisés sont considérés comme plus importants que la simple résolution du problème. La suringénierie existe.
James
2
@DeadMG: alors que vous avez tous les deux raison, dans le cas d'OP, le problème passe l'examen, et la solution à ce problème est d'utiliser des modèles de conception et UML. Peu importe à quel point nous avons raison, si son professeur n'est pas d'accord avec ses solutions, il ne passera pas.
Goran Jovic
2
Toute ma vie, j'ai toujours pensé que c'était la "taxe sur les cuivres" ... Wow, les "punaises sur les cuivres" ont beaucoup plus de sens. lol
acheté777