2010-11-04 3 views
2

Si j'ai une classe Container qui contient un tas d'objets Component (similaire au framework UI de Java), est-ce que ce serait un mauvais style/pratique de garder une trace du parent Container dans chaque Component? Cela finit par provoquer des problèmes de compilation étranges car le Component inclut le fichier d'en-tête Container et vice-versa. Même avec une correction de garde d'en-tête, je finis par devoir déclarer un prototype class Component; au-dessus de l'en-tête Container, et de même pour Component.Est-ce une mauvaise pratique d'utiliser des classes co-dépendantes?

Il semble que vous ayez à passer pas mal de temps pour obtenir une interaction bidirectionnelle entre deux classes. Serait-il conseillé de chercher une autre solution au problème que la co-dépendance "résout" ou est-ce que cette implémentation alambiquée est attendue de C++ et que je devrais juste l'aspirer?

EDIT: Peut-être qu'un contexte/raisonnement aiderait. La raison pour laquelle j'utilise cette co-dépendance est que je dois notifier le parent quand le destructeur d'un enfant a été appelé (afin qu'il puisse être retiré de la liste des enfants), et j'ai aussi besoin d'avoir la position dessinée de l'enfant être relatif à celui du parent.

Merci,
Jengerer

+0

Je retire l'étiquette C, parce que cela est vraiment une question de C++. –

+0

Pas évident pour moi que l'étiquette GUI soit appropriée - laissera à Jengerer de reconsidérer cela. –

+0

Eh bien, les classes 'Component' et' Container' sont pour une interface graphique sur laquelle je travaille en C++. Je suis d'accord que ce n'est pas tout ce qui est nécessaire, cependant. – Jengerer

Répondre

2

En fait, je pense que c'est un problème de conception. Vous devriez vraiment regarder le design dans son ensemble et voir si vous pouvez le concevoir mieux. Demandez-vous pourquoi vous avez les classes dépendantes nécessitant un accès au conteneur. Peut-être que le conteneur fournit des fonctionnalités qu'il ne devrait pas. Cette fonctionnalité peut-elle être fournie d'une autre manière?

+0

Les principaux problèmes que j'ai essayé de résoudre en utilisant cette conception étaient: a) Quand un 'Component' est détruit, je veux informer le parent qu'il devrait être retiré de la liste des enfants. b) Lorsqu'un 'Component' est dessiné à l'écran, je veux qu'il se dessine par rapport à la position du parent. Comment pourrais-je résoudre ces problèmes sans utiliser ces classes dépendantes? Je ne peux pas vraiment penser à une façon beaucoup plus propre que celle-ci. – Jengerer

+1

Hmm, Premièrement - où est le code qui détruit le composant? Peut-être que cela devrait être une fonction du conteneur afin qu'il puisse à la fois supprimer le composant et le retirer de la liste locale. Et la même chose avec la fonction de tirage. La fonction de dessin des conteneurs peut demander à chaque composant de dessiner, en lui donnant une position x/y pour commencer à. En d'autres termes, avec les fonctions supprimer et dessiner, pourquoi ne pas faire du conteneur le point d'entrée principal. Une autre option consiste à utiliser un modèle d'observateur dans lequel la référence au conteneur se trouve dans une interface. – drekka

+0

Bonne idée, je pense que je peux finir par aller avec cette suggestion. J'ai effectivement envisagé de placer la position de l'enfant à l'extérieur, mais j'ai hésité parce que je n'aimais pas devoir la calculer à chaque fois, mais j'ai réalisé que c'est déjà ce que mon programme fait, seule la position est calculée à la place. – Jengerer

2

Il est généralement préférable d'avoir pas de telles dépendances cycliques: ils sont un peu d'une douleur au code, frustrer le raisonnement logique sur le code, etc .. testabilité

Ils peut être facilement évité de toute façon - vous pouvez garder certains foncteurs dans les Composants, les pointer sur les opérations Conteneur appropriées aux événements traités, ou dériver le Conteneur d'une interface abstraite, ainsi les Composants peuvent avoir un pointeur vers lui et le Conteneur en dériver. Ou bien les composants peuvent être stockés par un pointeur (intelligent), avec un type dérivé implémentant des gestionnaires d'événements virtuels personnalisés pour le conteneur. Il y a beaucoup d'autres possibilités, par ex. Si vous voulez éviter le surcoût lié à l'exécution, prenez en compte les classes de stratégies CRTP ou de modèle.

2

Avoir un composant garder un pointeur sur son parent est largement utilisé, et je ne le vois pas comme une mauvaise pratique. Il peut être utilisé pour une variété de choses, du travail des arbres de widgets à la manipulation des composants perdus ou fui. Pour contourner le problème avec le compilateur, vous devez soit placer les lignes d'inclusion dans les protections d'en-tête, soit avoir un troisième fichier avec des déclarations directes pour les deux classes (dans l'espace de noms approprié).

J'ai vu ce dernier fait dans quelques grandes bibliothèques OO, et il est généralement inclus dans presque tous les autres fichiers du projet. Bien que cela puisse conduire à un peu de pollution de l'espace de noms, cela présente certains avantages (sans avoir besoin d'inclure la définition de classe complète ou les dépendances d'une autre classe juste pour avoir un pointeur dessus).

Widget.hpp:

#include "Library.hpp" 

namespace MyLibrary 
{ 
    class Widget 
    { 
    private: 
     Window * parent; 
    }; 
} 

fenêtre.HPP:

#include "Library.hpp" 

namespace MyLibrary 
{ 
    class Window 
    { 
    private: 
     std::vector<Widget*> widgets; 
    }; 
} 

Library.hpp:

namespace MyLibrary 
{ 
    class Window; 
    class Widget; 
} 
+0

Bonne suggestion. Je vais probablement aller avec la suggestion de Derek car elle semble généralement plus facile à mettre en œuvre tout en continuant à faire le même travail. Merci pour la contribution, cependant! – Jengerer

Questions connexes