Disons que j'ai deux types d'objets, A et B. La relation entre eux est plusieurs-à-plusieurs, mais aucun d'eux n'est propriétaire de l'autre.
Les instances A et B doivent être conscientes de la connexion; ce n'est pas qu'une façon.
Donc, nous pouvons le faire:
class A
{
...
private: std::vector<B *> Bs;
}
class B
{
private: std::vector<A *> As;
}
Ma question est: où dois-je mettre les fonctions pour créer et détruire les connexions?
Faut-il que ce soit A :: Attach (B), qui met alors à jour A :: Bs et B :: As vecteurs?
Ou devrait-il être B :: Attach (A), ce qui semble tout aussi raisonnable.
Aucun de ceux-là ne se sent bien. Si j'arrête de travailler avec le code et que je reviens après une semaine, je suis sûr que je ne pourrai pas me rappeler si je devrais faire A.Attach (B) ou B.Attach (A).
Peut-être que cela devrait être une fonction comme celle-ci:
CreateConnection(A, B);
Mais créer une fonction globale semble également indésirable, étant donné qu'il s'agit d'une fonction spécifique pour travailler uniquement avec les classes A et B.
Une autre question: si je rencontre souvent ce problème / cette exigence, puis-je trouver une solution générale? Peut-être une classe TwoWayConnection dont je peux dériver ou utiliser au sein de classes qui partagent ce type de relation?
Quels sont les bons moyens de gérer cette situation ... Je sais comment gérer très bien la situation un-à-plusieurs "C possède D", mais celle-ci est plus délicate.
Edit: Juste pour le rendre plus explicite, cette question n'implique pas de problèmes de propriété. A et B appartiennent à un autre objet Z, et Z s'occupe de tous les problèmes de propriété. Je veux seulement savoir comment créer / supprimer les liens plusieurs-à-plusieurs entre A et B.
Pointer
etGestureRecognizer
. Les pointeurs sont détenus et gérés par la classe InputManager. GestureRecognizers appartiennent à des instances de Widget, qui sont à leur tour détenues par une instance d'écran qui appartient à une instance d'application. Les pointeurs sont assignés à GestureRecognizers afin qu'ils puissent leur fournir des données d'entrée brutes, mais GestureRecognizers doit être conscient du nombre de pointeurs qui leur sont actuellement associés (pour distinguer 1 doigt contre 2 gestes du doigt, etc.).Réponses:
Une façon consiste à ajouter une
Attach()
méthode publique et également uneAttachWithoutReciprocating()
méthode protégée à chaque classe. FaitesA
-vousB
des amis communs pour que leursAttach()
méthodes puissent appeler les autresAttachWithoutReciprocating()
:Si vous implémentez des méthodes similaires pour
B
, vous n'aurez pas à vous souvenir de la classe à appelerAttach()
.Je suis sûr que vous pouvez envelopper ce comportement dans une
MutuallyAttachable
classe à la foisA
etB
Hériter de, évitant ainsi de vous répéter et marquer des points bonus le jour du jugement. Mais même l'approche peu sophistiquée de l'implémentation aux deux endroits fera le travail.la source
MutuallyAttachable
classe que j'ai écrite pour cette tâche, si quelqu'un veut la réutiliser: goo.gl/VY9RB (Pastebin) Edit: Ce code pourrait être amélioré en en faisant une classe modèle.MutuallyAttachable<T, U>
modèle de classe à Gist. gist.github.com/3308058Est-il possible que la relation elle-même ait des propriétés supplémentaires?
Si c'est le cas, ce devrait être une classe distincte.
Si ce n'est pas le cas, tout mécanisme de gestion de liste réciproque suffira.
la source
Habituellement, lorsque je me retrouve dans des situations comme celle-ci qui me semblent gênantes, mais je ne peux pas vraiment dire pourquoi, c'est parce que j'essaie de mettre quelque chose dans la mauvaise classe. Il y a presque toujours un moyen de sortir certaines fonctionnalités de la classe
A
etB
de rendre les choses plus claires.Un candidat pour déplacer l'association vers est le code qui crée l'association en premier lieu. Peut-être qu'au lieu d'appeler,
attach()
il stocke simplement une liste destd::pair<A, B>
. S'il y a plusieurs endroits créant des associations, il peut être résumé en une seule classe.Un autre candidat pour un mouvement est l'objet qui possède les objets associés,
Z
dans votre cas.Un autre candidat commun pour déplacer l'association est le code qui est souvent appeler des méthodes sur
A
ouB
objets. Par exemple, au lieu d'appelera->foo()
, ce qui fait en interne quelque chose avec tous lesB
objets associés , il connaît déjà les associations et appellea->foo(b)
pour chacun. Encore une fois, si plusieurs endroits l'appellent, il peut être résumé dans une seule classe.Souvent, les objets qui créent, possèdent et utilisent l'association se trouvent être le même objet. Cela peut rendre le refactoring très facile.
La dernière méthode consiste à dériver la relation à partir d'autres relations. Par exemple, les frères et sœurs sont une relation plusieurs-à-plusieurs, mais plutôt que de conserver une liste de sœurs dans la classe des frères et vice versa, vous dérivez cette relation de la relation parentale. L'autre avantage de cette méthode est qu'elle crée une source unique de vérité. Il n'y a aucun moyen possible pour un bug ou une erreur d'exécution de créer une différence entre les listes frère, sœur et parent, car vous n'avez qu'une seule liste.
Ce type de refactoring n'est pas une formule magique qui fonctionne à chaque fois. Vous devez encore expérimenter jusqu'à ce que vous trouviez un bon ajustement pour chaque circonstance individuelle, mais je l'ai trouvé fonctionner environ 95% du temps. Les temps restants, il vous suffit de vivre avec la maladresse.
la source
Si j'implémentais cela, je mettrais Attach à la fois A et B. À l'intérieur de la méthode Attach, j'appellerais alors Attach sur l'objet transmis. De cette façon, vous pouvez appeler A.Attach (B) ou B.Attach ( UNE). Ma syntaxe n'est peut-être pas correcte, je n'ai pas utilisé le c ++ depuis des années:
Une autre méthode consisterait à créer une 3e classe, C qui gère une liste statique de paires AB.
la source