J'écris un jeu où l'objet contrôleur piloté de la souris clique sur un objet de joueur pour le faire faire quelque chose. P>
Il y a 2 façons d'initier l'interaction entre la souris et le joueur: P>
Mon dilemme Voici que la première option semble plus intuitive avec la façon dont j'imagine le scénario qui se produise dans le monde réel, mais la deuxième option semble plus intuitive avec une conception appropriée à orientation objet, car elle ne nécessite pas d'aborder la propriété d'un autre objet , qui enfreint l'encapsulation dans une certaine mesure (le contrôleur doit examiner le joueur pour lire sa propriété "cliquable"). En outre, la deuxième option semble en ligne avec le modèle de conception de «contrôleur». P>
C'est toujours une lutte pour moi - puis-je défier une conception appropriée orientée objet (option 1) ou est-ce que j'utilise une implémentation qui semble contre-intuitive au monde réel (par exemple l'option 2)? P>
J'espère qu'il y a une sorte de terrain moyen, je manque. P>
11 Réponses :
Cela vient souvent de préférence. La conception de jeux logiques est très fréquemment en attente avec une bonne conception OOP. Je me penche vers ce qui est logique dans la portée de l'univers du jeu, mais il n'y a absolument aucune bonne réponse à la question et que chaque problème devrait être pris sur son propre mérite. P>
Ceci est un peu similaire à se disputer sur les avantages et les inconvénients du boîtier de chameau. P>
Concept MVC est une bonne pratique en général et devrait toujours être utilisé dans des conceptions de jeux comme principe général. Mais par la nature interactive de l'interface utilisateur du jeu, vous devriez également examiner l'architecture basée sur les événements. MVC ne contredit pas avec une architecture basée sur les événements si conçue soigneusement. (Puremvc comme exemple.) P>
Je vous suggère d'utiliser un motif observable sur tous les objets d'affichage afin qu'ils puissent tous écouter / feu événements. Cela vous fera économiser beaucoup de mal de tête plus tard. Lorsque votre base de code devient plus complexe, vous devez éventuellement utiliser plus de techniques de découplage tels que votre option 2 décrit. Aussi Modèle de médiateur aidera également. P>
EDIT: P>
Modèle de médiateur est généralement un bon moyen d'organiser des événements de niveau d'application. p>
Voici un blog sur l'utilisation de MVC, des événements et des médiateurs dans la programmation de jeux: P>
Peux-tu élaborer? Peut-être fournir un court exemple? Je connais l'architecture basée sur les événements et vous verrez que les deux scénarios utilisent des événements et des auditeurs (mettant ainsi en œuvre «Observateur»). Alors, je pensais que je l'utilisais.
Edité mon message pour fournir d'autres informations, espérons que cela vous aidera. En outre, je pense que vous utilisez un motif observable
Pourquoi ne pas aller avec l'option 3, qui est SIMLAR à l'option 1? p>
En fait, j'utilise cette option. J'ai exclu les interfaces de mon exemple car ils ne semblent que masquer le cœur de ma lutte.
@PUP, il y a une grande différence et je préférerais l'option 3 à l'option 1 n'importe quel jour.
Cela n'accueille pas votre lutte. La mise en œuvre de la machine fikable (ou quelque chose d'équivalent) rend une partie de cliquée de l'interface de l'objet, qui interrogeant ainsi la cliquabilité d'un objet n'est pas en violation de l'encapsulation. Fin de la lutte. En outre, j'interpréterais le "Tell, ne demande pas" -principle comme indiquant que le joueur doit être raconté qu'il a été cliqué plutôt que de demander à avoir été cliqué.
En outre, n'oubliez pas que l'un des principes les plus importants d'OOT est polymorphisme i>. Ceci est définitivement à 100% du moyen d'aller - à partir d'un point de vue théorique de la théorie et d'un pov pragmatique.
Slashmais - Bien que une partie de votre réponse soit vraie, ce qui manque, c'est que le contrôleur ne saura souvent pas quelles actions à prendre! Et donc, l'objet joueur est celui qui doit faire cette décision. Par exemple, un type d'objet peut sauter, un autre sera sélectionné et flash, un autre pourrait simplement exploser. Le contrôleur ne peut en effet pas savoir quelles actions à prendre. De plus, le contrôleur ne devrait pas i> savoir. C'est à quoi ressort de la délégation de contrôle.
D'autre part, ce qui pourrait être encore mieux, est de découpler les cours de joueur de manipuler l'interface utilisateur, mais de ne pas le mettre dans le contrôleur - peut-être diviser chaque classe en deux (selon les besoins): une pour gérer l'interface utilisateur (le cliquable une partie de la classe), et celle-ci saura interpréter ces événements et quelles méthodes pour faire appel à la deuxième partie de la catégorie des applications de la classe.
C'est toujours une lutte pour moi - puis-je défier une conception appropriée orientée objet (option 1) ou est-ce que j'utilise une implémentation qui semble contre-intuitive au monde réel (par exemple l'option 2)? P> blockQuote>
Je ne pense pas que le but de l'orientation objet est de modéliser le monde réel. P>
Je pense qu'une raison (la?) Pourquoi un modèle OO suit souvent un modèle du monde réel est que le monde réel ne change pas beaucoup: et choisissant donc le monde réel comme le modèle signifie que le logiciel signifie que le logiciel signifie que le logiciel ne changera pas grand chose, c'est-à-dire qu'il sera peu coûteux de maintenir. p>
La fidélité au monde réel n'est pas un objectif de conception en soi: au lieu de cela, vous devriez essayer de concevoir des conceptions qui optimisent d'autres métriques, par ex. Simplicité. P>
Les "objets" dans "l'orientation d'objet" sont logiciels em> objets, pas nécessairement des objets du monde réel. P>
C'était très utile pour moi! J'ai oublié la dichotomie fondamentale entre la communication informatique et verbale - les interactions informatiques sont très impersonnelles; Lorsque vous interagissez avec un objet, vous le direz de faire quelque chose avec lui-même. Alors que dans la grammaire anglaise, un sujet porte la responsabilité de l'action sur son objet direct; Vous ne diriez pas que l'action est faite par l'objet direct, par ex. un ballon. La même philosophie s'applique ici - ne vous attendez pas à ce que l'instigateur d'action soit le réaliser, il sera plutôt à un objet d'agir sur lui-même. Toujours indécis.
+1 oop n'a jamais été conçu pour modéliser le monde réel. Il a été conçu pour permettre une plus grande réutilisation du code (qui n'a pas vraiment fonctionné bien). Vous ne devriez pas essayer de modéliser le monde réel lorsque vous concevez quelque chose (sinon, la plupart des modèles de OOP n'auraient pas de sens). Tant que cela fonctionne bien et a du sens, ne vous inquiétez pas d'essayer de le rendre conforme à un dogme de design particulier.
@Snorfus Vérifiez vos faits. La réutilisation du code avec les langues orientées objet a fonctionné fabuleusement. OO Langues Puissance Presque toutes les trousses d'interface graphique à ce jour. Sans réutiliser ces kits à outils ne seraient pas beaucoup d'usage. Les langues OO ont été les premières à fournir une bibliothèque de code réutilisable intégré à la plate-forme. SmallTalk, Java, Ruby, tous sont livrés avec un code prêt que vous n'avez pas à vous écrire. La réutilisation du code a été un objectif bien atteint des langues de OO.
La deuxième méthode est la méthode flash résolument plus idiomatique de remédier à des choses. AS3 a le modèle d'événement construit directement dans Pensez au lecteur Flash comme contrôleur. Lorsque je fais MVC en Flash, je n'écris presque jamais de contrôleur car le lecteur Flash le fait pour vous. Vous perdez des cycles déterminant ce qui a été cliqué lorsque le lecteur Flash sait déjà. P> Je n'aurais probablement pas directement accès à un contrôleur d'une sprite (probablement dans la vue ). Au lieu de cela, je disposerais d'un événement (probablement un événement personnalisé spécifique qui correspondait à la circonstance). Effectuez vos décisions sur le nombre de fois par image. Répondre à une interaction utilisateur (par exemple, la souris-clic) vous donne généralement la liberté de ne pas vous inquiéter des frais généraux dans les systèmes d'événement. P> Enfin - la raison pour laquelle je suggère que cela n'a rien à voir avec du concept de la tour d'ivoire de Ood ou oop. Les principes tels que ceux-ci existent pour vous aider à ne pas vous contraindre. Lorsqu'il s'agit d'une question de pragmatique, accédez à la solution la plus facile qui ne vous causera pas de maux de tête dans la ligne. Parfois, cela signifie faire du oop, parfois cela signifie fonctionnel, parfois cela signifie impératif. P> p> eventDispatcher code> et tous
displayObjects code> hériter de celui-ci. Cela signifie tout
bitmap code>,
sprite code> ou
MOVIECLIP code> sait immédiatement s'il est cliqué sur.
"Je n'aurais probablement pas directement accès à un contrôleur de l'intérieur d'un sprite (probablement dans la vue). Au lieu de cela, je m'établirais un événement (probablement un événement personnalisé spécifique qui correspondait à la circonstance)." Donc, vous voulez dire que vous envoyez un événement personnalisé à partir de l'objet du lecteur que le contrôleur écouterait la liste d'affichage?
Cela dépend vraiment de la hiérarchie de votre objet. J'ai tendance à ne pas nier les choses trop profondément (ce qui est une bonne idée de toute façon) pour que je puisse facilement faire passer manuellement des événements de bulle à un niveau complet. Je crée également des classes de "vue" qui agrégent logiquement plusieurs objets d'affichage ensemble. Donc, typiquement une vue écoutera, il s'agit de Son DisplayObjects et il s'interfacera avec le modèle en leur nom.
Je pense, il est important de séparer la logique d'entrée à partir de la logique de l'application ... Une approche consiste à convertir des événements d'entrée (être une entrée utilisateur informatique, ou certaines données arrivant via des sockets / localConnections, etc.) dans des événements d'application (événements dans un Signification abstraite du mot) ... Cette conversion est effectuée par ce que j'appellerais "contrôleurs avant" pour l'absence de meilleur terme ... P>
Tous ces contrôleurs avant convertissent simplement des événements et sont donc complètement indépendants de la manière dont la logique d'application répond à des événements spécifiques ... La logique d'application de l'autre main est découplée de ces contrôleurs avant ... Le "protocole" est un ensemble prédéfini d'événements ... En ce qui concerne la notification MecToriZM, il vous appartient si vous utilisez l'envoi de l'événement AS3 pour obtenir des événements des contrôleurs avant vers des contrôleurs d'application ou si vous les construisez à une interface spécifiée, qu'ils appellera à ... p>
Les gens ont tendance à écrire la logique d'application dans des touches de clic pour les boutons ... et parfois même pour déclencher ces manuels manuellement, car ils ne veulent pas ranger les choses ... rien que je n'ai pas encore vu ... p>
Alors oui, c'est définitivement la version 1 ... Dans ce cas, le contrôleur avant pour les entrées de la souris n'a besoin que de connaître la liste d'affichage et de disposer de la logique lorsque vous devez expédier quel événement ... et le contrôleur de conviction doit être capable de gérer une sorte de J'espère que cela vous aide ...;) p> playerevent.select code> événement dans ce cas ... (Si vous décidez plus tard, avoir une sorte de mode de tutoriel, ou autre, vous pouvez simplement vous déplacer autour de la fausse souris et de l'envoi Cet événement dans le cas de faux clics, ou vous pouvez simplement répéter tout ce qui est dans une sorte de replay, ou vous pouvez utiliser ceci pour enregistrer des macros, quand il ne s'agit pas de jeux ... juste pour signaler certains scénarios où cette séparation est utile ) p>
Selon Appliquer UML et modèles ( Craig Larman) L'interface utilisateur (vos événements de souris) ne doit jamais interagir avec vos classes d'application, c'est-à-dire que l'interface utilisateur ne doit jamais conduire directement la logique commerciale. P>
Au lieu de cela, un ou plusieurs contrôleurs doivent être définis pour servir de couche intermédiaire à l'interface utilisateur, l'option 1 suive en effet une bonne approche orientée objet. P>
Si vous y réfléchissez, il est logique de quelques classes que possible à l'interface utilisateur afin de rendre la logique commerciale aussi indépendante de l'UI que possible. P>
Je dois être en désaccord avec votre évaluation des deux options. L'option 2 est pire OO car elle coule étroitement l'objet du joueur à la mise en œuvre spécifique de l'interface utilisateur. Que se passe-t-il lorsque vous souhaitez réutiliser votre classe de joueur quelque part où elle est exclusive ou une interface sans souris est utilisée? P>
L'option 1 peut toujours être améliorée, cependant. La suggestion antérieure de l'utilisation d'une interface incitable ou d'une superclasse cliquable est une amélioration immense, car elle vous permet de mettre en œuvre plusieurs types d'objets cliquables (pas seulement du joueur) sans avoir à donner au contrôleur une énorme liste de "est l'objet de cette classe? C'est cette classe? " passer à travers. p>
Votre objection principale à l'option 1 semble être qu'elle vérifie la propriété "Clickable" du joueur, que vous ressentez enfreindre l'encapsulation. Ce n'est pas le cas. Il vérifie une propriété qui est définie dans le cadre de l'interface publique du joueur. Ce n'est pas différent d'appeler une méthode de l'interface publique. P>
Oui, je me rends compte que, à ce moment-là, la propriété "Clickable" se trouve être mise en œuvre de telle sorte qu'il s'agit d'un getter simple qui ne fait rien de plus que d'interroger l'état interne du joueur, mais il n'a pas être em>. La propriété pourrait être redéfinie demain pour déterminer sa valeur de retour de manière totalement différente, sans référence à l'état interne et, tant qu'il retourne toujours un booléen (c'est-à-dire que l'interface publique reste la même), code utilisant le joueur.Clickable Travaille toujours bien. C'est la différence entre une propriété et une inspection directe de l'état interne - et c'est une grande différence. P>
Si cela vous rend encore mal à l'aise, il est suffisamment facile d'éliminer la vérification du contrôleur.Clickable: envoyez simplement l'événement de clic à chaque objet sous la souris qui implémente de manière cliquable / descend de Clickable. Si l'objet est dans un état non cliquable lorsqu'il reçoit le clic, il peut simplement l'ignorer. P>
Dans n'importe quel langage OOP, c'est presque toujours la bonne chose à faire pour suivre l'approche idiomatique lorsqu'il y a aussi la possibilité de suivre l'approche d'émulation de la vie réelle. Donc, pour répondre à votre question, la deuxième approche est presque certainement meilleure, ou peut s'avérer mieux que vous approfondissez de plus en plus profondément dans la conception ou de ressentir la nécessité de le changer ou de l'ajouter. P>
Cela ne devrait pas vous empêcher de trouver d'autres solutions. Mais essayez de toujours rester avec l'idiome de langue. OOP ne se traduit pas vers une vie réelle dans une relation de 1 à 1, ni elle n'est particulièrement bonne à l'émuler. Un exemple illustratif est le rectangle classique et la relation d'objet carré que vous connaissez probablement déjà tout. Dans la vie réelle, un carré est un rectangle. Dans OOP (ou au moins dans la bonne opération), cette relation ne se traduit pas dans une relation dérivée de base. Vous ressentez donc la nécessité de vous échapper de l'émulation de la vie réelle, car l'idiome de langue parle plus haut. C'est soit cela, soit un monde de douleur lorsque vous commencez sérieusement à mettre en œuvre à la fois rectangle et carré ou souhaite ultérieurement de leur apporter des modifications. p>
Rectangle / La relation carrée me semble bien ... attention à expliquer ou à citer une autre analogie?
Essentiellement, dans la plupart des situations, si vous décidez qu'un carré est un type de rectangle que vous rencontrez des problèmes. C'est-à-dire que vous découlez du carré de rectangle, vous serez cartographier la relation taxonomique dans une règle IS-A. Mais:
C'est toujours une lutte pour moi - puis-je défier une conception appropriée orientée objet (option 1) ou est-ce que j'utilise une implémentation qui semble contre-intuitive au monde réel (par exemple l'option 2)? P> blockQuote>
La réalité peut être un bon point de départ pour le moulage ou l'évolution d'une conception, mais c'est toujours une erreur de modéliser une conception OO à la réalité. P>
La conception OO concerne les interfaces, les objets qui les implémentent et l'interaction entre ces objets (les messages qu'ils transmettent entre eux). Les interfaces sont des accords contractuels entre deux composants, modules ou sous-systèmes logiciels. Il existe de nombreuses qualités à une conception OO, mais la qualité la plus importante pour moi est la substitution. Em> si j'ai une interface, le code de mise en œuvre est mieux adhérant à celui-ci. Mais plus important encore, si la mise en œuvre est échangée, la nouvelle mise en œuvre est mieux adhérée. Enfin, si la mise en œuvre est censée être polymorphe, les différentes stratégies et états de la mise en œuvre polymorphe adhèrent mieux à cela. P>
Exemple 1 P>
en mathématiques, un carré
est un rectangle fort>. Cela ressemble à une bonne idée d'hériter de la place de classe à partir du rectangle de classe. Vous le faites et cela mène à la ruine. Pourquoi? Parce que l'attente ou la croyance du client a été violée. La largeur et la hauteur peuvent varier de manière idéale mais la carré viole ce contrat. J'ai eu un rectangle de dimension (10, 10) et je mets la largeur sur 20. Maintenant, je pense em> j'ai un rectangle de dimension (20, 10) mais l'instance réelle est une instance carrée avec des dimensions (20, 20) et moi, le client, je suis pour un réel em> grosse surprise. Alors maintenant, nous avons une violation du principe de moins surprise. Em> p> Vous avez maintenant un comportement de buggy, ce qui conduit à un code client devient complexe comme si des déclarations sont nécessaires pour contourner le comportement de la buggy. Vous pouvez également trouver votre code client exigeant que RTTI fonctionne autour du comportement de buggy en testant des types de Conréte (j'ai une référence à recenser, mais je dois vérifier s'il s'agit d'une instance carrée). P>
Exemple 2 P>
Dans les animaux de vie réels peuvent être des carnivores ou des herbivores. Dans la vraie viande et les légumes sont des types de nourriture. Vous pourriez donc penser que c'est une bonne idée d'avoir un animal de classe en tant que classe mère pour différents types d'animaux. Vous pensez également que c'est une bonne idée d'avoir une classe de parents de nourriture pour la viande de classe et des légumes de classe. Enfin, vous avez un sport d'animal de classe une méthode appelée manger (), em> qui accepte une alimentation comme un argument formel. P>
Tout compile, passe une analyse statique et des liens. Vous exécutez votre programme. Que se passe-t-il au moment de l'exécution lorsqu'un sous-type d'animal, disons un herbivore, reçoit une alimentation qui est une instance de la classe de viande? Bienvenue dans le monde de la convivialité et de la contrevenue. Ceci est un problème pour de nombreuses langues de programmation. C'est aussi un problème intéressant et stimulant pour les concepteurs de langue. P>
En conclusion ... P>
Alors que faites-vous? Vous commencez avec votre domaine problématique, vos histoires d'utilisateurs, vos cas d'utilisation et vos besoins. Laissez-les conduire. Laissez-les vous aider à découvrir les entités dont vous avez besoin pour modéliser des classes et des interfaces. Lorsque vous ferez, vous constaterez que le résultat final n'est pas basé sur la réalité. P>
Check out Modèles d'analyse par Martin Fowler . Dans y compris, vous verrez ce qui motive ses conceptions orientées objet. Il repose principalement sur la manière dont ses clients (personnes médicales, personnes financières, etc.) effectuent leurs tâches quotidiennes. Il a chevauchement avec la réalité, mais elle n'est pas basée ou motivée par la réalité. P>
Une autre approche serait de créer une interface ICLickHandler. Tous les objets qui s'inscrivent pour clics le font en remettant un iClickHandler sur le contrôleur. Lorsqu'un objet a cliqué sur le contrôleur appelle la méthode cliquée () sur l'iClickhandler enregistré. L'ICLickHandler peut transmettre ou ne pas transmettre l'appel à la méthode enregistrée avec elle. Maintenant, ni votre contrôleur ni votre objet prennent la décision de déterminer si ledit objet a été véritablement cliqué. P>
L'iClickhandler pourrait également être choisi sur la base d'autres critères (iow L'objet ne choisit pas l'ICLickHandler lui-même lors de l'inscription, un autre algorithme le choisit). Un bon exemple serait que tous les objets NPC sont donnés à un iClickhandler qui transfère les clics, tandis que tous les arbres reçoivent un iClickhandler qui ne transfère pas les clics. P>
Au minimum, vous pourriez avoir 3 manutentionnaires qui implémentent l'interface: toujoursClickable, NickClickable, basculable p>
Il suffit de garder à l'esprit que ce qui précède fait plus de pièces mobiles ainsi qu'un petit succès de performances, mais cela vous donne beaucoup de flexibilité (c'est à vous de décider de décider si la flexibilité vaut la complexité supplémentaire). p>
Notez également qu'il est préférable de ne pas simplement adhérer aux principes de tout type. Faites ce qui est le mieux compte des circonstances au moment où vous écrivez le code. Si vous écrivez un clone Tetris, le fait que l'option 1 "violent" les principes du POO est complètement non pertinentes, vous ne réaliserez jamais les avantages de Strict OOP sur un projet simple. P>
Pourquoi l'option 1 défierait-elle une conception appropriée de OOP? Un objet est certainement autorisé à lire des propriétés publiques d'autres objets. C'est pourquoi ils sont publics!
Une autre question - si l'objet joueur écoute directement les événements de la souris, quel sera le point d'un objet contrôleur?
L'option 1 défie OOP Principes i> Parce que c'est souvent un signe révélateur que les responsabilités sont désactivées lorsqu'un objet examine un autre interne d'objets (par exemple via une fonction 'getter'). Un bon principe de OOP est "Ne demandez pas - raconter" parce que quel que soit l'objet a i> les informations pour une tâche devraient être la seule utilisation de ces informations car elles sont l'information "expert", comme référence dans "Appliquer UML et motifs" de Craig Larman.
@Vilx: L'objet Controller fournit des comportements pour la manière de manipuler le joueur. Ceci est un exemple du modèle «contrôleur» où les contrôleurs peuvent être échangés à volonté. Par exemple, si mon joueur ne veut pas être contrôlé à une certaine heure, il pourrait choisir de pas i> apporter l'appel au contrôleur pour la manipulation.
@PUP, si cet absolutisme doit être pris, quel est le but des propriétés publiques?