2010-09-19 28 views
1

Je rencontre un petit problème de dépendance circulaire. Cela fonctionne bien, mais cela donne un code apparemment laid. C'est dans le contexte d'un jeu de Snake.Jeu C++ - Signalisation d'une classe parent, problème de dépendance circulaire

J'ai une classe, Snake, qui contient un vecteur de SnakeSegments, et gère leur interaction (par exemple, se déplacer et grandir en tant qu'unité, plutôt que comme des entités séparées).

Lorsqu'un SnakeSegment entre en collision avec un objet Food, il retourne son membre hasEaten à true. Le serpent interroge régulièrement le SnakeSegments, essentiellement pour ce membre. Si l'une quelconque des requêtes revient positive (c'est-à-dire que l'une a heurté la nourriture), alors le serpent grandira en tant qu'unité (c'est-à-dire élargira la tête et rétrécira la queue). C'est très bien, mais je préfère de loin une approche plus basée sur le signal, où, quand un SnakeSegment touche de la nourriture, il envoie une alerte (signal, interruption, etc.) à la classe Snake, qui lui dit de grandir . Cela signifie que je n'aurais pas de code moche dans ma fonction de mise à jour de Snake, en vérifiant tous les segments; et je voudrais à la place avoir une fonction OnEat() dans ma classe Snake.

Cependant, cela conduit à des dépendances circulaires; le serpent contient un vecteur de SnakeSegments, et les SnakeSegments ont un membre Snake & ou Snake *, qui leur indique qui alerter quand ils mangent. Dans le code, j'ai essentiellement juste d'effectuer une pré-déclarer la classe Serpent:

class Snake; 
class SnakeSegment 
{ 
... 
Snake* alertOnEat; 
... 
};

et ma classe Snake fonctionne normalement juste

#include "SnakeSegment.hpp" 

class Snake 
{ 
... 
std::vector segments; 
... 
void OnEat(); 
... 
};

Y at-il plus agréable dessins à cela? Notez que ce n'est pas seulement un problème qui se produit ici; un problème similaire se produit dans un certain nombre de domaines (par exemple le GameWorld contient un membre Snake, et le Snake alerte le GameWorld quand il meurt), donc une solution spécifique à Snake et SnakeSegment n'est pas ce que je cherche.

Répondre

3

Ce que vous pouvez vouloir regarder pour ce problème est le modèle de conception Observateur/Observable. Il vous permet de créer des objets qui observent (Snake) des objets observables (SnakeSegment) et d'être notifiés dès que leur état a changé.

Wikipedia a un bon exemple écrit dans de nombreuses langues, y compris C++.

Il s'agit d'un modèle commun utilisé dans de nombreux développements d'interface graphique afin que le calque Vue puisse changer lorsque l'une des données du modèle sous-jacent change.

4

Votre design actuel est parfaitement adapté à la plupart des situations. Oui, cela crée une dépendance circulaire, mais c'est aussi la manière la plus simple et la plus claire de le faire. Toutefois, vous devez limiter ces dépendances entre les interfaces ou les classes de base plutôt que directement aux classes dérivées. La dépendance du monde-serpent est un bon exemple. Vous ne souhaitez pas nécessairement que la classe World connaisse tous les types d'objets de jeu possibles. Cependant, ce que vous pouvez faire est de dériver tous vos objets de jeu d'une classe GameObject commune et de faire World et GameObject interdependant.

Il existe également des façons plus complexes d'éviter les dépendances, avec leurs avantages et leurs inconvénients, elles diffèrent principalement sur le plan de la séparation et de la clarté. En fin de compte, il s'agit toujours de la complexité que vous êtes prêt à introduire dans votre architecture en échange de flexibilité et de séparation claire des préoccupations.

Ne jamais oublier que la conception est compromettre.

+0

Pourriez-vous expliquer pourquoi il est préférable de créer des dépendances entre les classes de base? – problemofficer

+0

L'objectif est de réduire les dépendances. Si vous liez uniquement les classes de bases (World et GameObject), vous n'avez pas besoin d'être dépendant de toutes les sous-classes possibles de GameObject. Fior instance, si un créer un Ennemy: GameObject je peux facilement l'ajouter au monde comme GameObject sans que le monde sache que cette sous-classe existe. – Coincoin