Conception: rappel à la classe parente

13

Lors de la modélisation d'un objet avec des enfants, il est courant d'inclure les enfants via la composition, en tant que membre de la classe parent. Parfois, cependant, les enfants doivent dire quelque chose au parent, ils doivent appeler une fonction du parent. Comment cela peut-il être accompli en utilisant C ++? Certaines options sont:

  1. Rendez la classe parente globale donc les objets enfants pourront appeler les fonctions membres de l'objet parent.

  2. Injectez l'objet parent en tant que pointeur ou référence dans chaque objet enfant. Ensuite, lorsque l'enfant a besoin de dire quelque chose à l'objet parent, il peut toujours le faire car il a une variable membre qu'il peut utiliser.

Quelles sont les autres méthodes pour ce faire? Existe-t-il un modèle de conception général ou un nom pour ce genre de chose?

Notez que je suis intéressé par les idées et les solutions en C ++ car les détails vont être différents dans d'autres langages orientés objet. Par exemple, le point 2 ci-dessus mentionne des «pointeurs ou références» et les deux ne sont possibles qu'en C ++. C ++ a des fonctionnalités de langage qui ne sont pas présentes dans d'autres langues, par conséquent, les implémentations d'une solution au problème incorporeront potentiellement ces fonctionnalités de langage, ce qui rend la solution différente de ce que quelqu'un pourrait proposer dans une autre langue.

sashang
la source
pouvez-vous ajouter un exemple? Est-ce un exemple valable pour votre question? Vous avez un objet de commande (= parent) avec des articles de commande (enfants) et souhaitez mettre à jour le total de la commande lorsqu'une quantité d'article de commande est modifiée? ou pensez-vous à quelque chose de complètement différent?
k3b
@ k3b Ouais, c'est un exemple valable. Certaines informations de l'enfant ont changé, obligeant le parent à faire quelque chose.
sashang
ajoutez simplement des paramètres de constructeur et des membres de données de référence à chaque classe enfant.
tp1
L'enfant a-t-il besoin de tout savoir sur le parent, ou un simple delegatesuffirait-il?
Julien Guertault

Réponses:

16

Tout d'abord, cela peut être une odeur de code. L'intérêt d'utiliser la composition pour le parent / les enfants est que le parent connaît les enfants mais pas l'inverse. Surtout si la relation est plus un «contient» que «est composé de».

Une référence au parent est possible et assez courante en C ++. Dans d'autres langages, un objet ou un événement de fonction est plus souvent utilisé pour permettre à l'enfant de communiquer des choses que des étrangers pourraient vouloir savoir. Il s'agit d'une sorte de modèle courant de publication-abonné. Je soupçonne que ce qui est plus idiomatique dépend de la version de C ++ que vous utilisez et des normes de votre base de code.

Telastyn
la source
Dans ma situation, c'est une odeur de code. Très bonne réponse.
Martin Pfeffer
3

Comme d'autres l'ont souligné, l'idée de base d'injecter l'objet parent comme pointeur ou référence est la voie à suivre - en principe.

Cela a un inconvénient: vous obtenez une dépendance cyclique entre le parent et l'enfant. Si vous voulez éviter cela, définissez une classe de base abstraite (une interface) IParentdont votre parent hérite. IParentdoit contenir les méthodes en tant que fonctions virtuelles que l'enfant souhaite appeler. Ensuite, injectez le parent comme référence à IParent. Cela rend le test unitaire de l'enfant beaucoup plus facile, car vous pouvez maintenant remplacer facilement l'objet parent par un objet factice.

Si votre enfant a besoin d'appeler une seule fonction de votre objet parent, une IParentclasse complète peut être surdimensionnée. Dans ce cas, il suffira d'injecter un pointeur vers la fonction membre dans l'enfant, ou un objet fonctor encapsulant cette fonction membre.

Doc Brown
la source
2

Ce que vous pouvez faire, c'est conserver une référence au parent dans la classe enfant, par composition. De cette façon, l'enfant connaît son parent et peut invoquer des méthodes publiques dessus.

Donc, j'irais avec l'option 2. Vous devez être prudent cependant, quand un enfant est supprimé du parent, vous devez supprimer la référence au parent dans l'enfant et la pointer vers null (ou un nouveau parent, s'il a un). Ou vous pouvez simplement supprimer l'objet enfant, selon le contexte.

marco-fiset
la source
1

Passez-leur une référence ou un pointeur au parent. Vous pouvez les faire amis du parent ou rendre publique la méthode appelée. Si vous ne voulez pas faire l'une des choses ci-dessus, vous pouvez leur passer un objet "bridge" qui expose l'une des méthodes du parent en tant que public et est lui-même une classe imbriquée privée du parent (donc il a accès à chaque méthode parent ). Cependant, cela peut être un peu trop complexe dans de nombreuses situations.

quant_dev
la source
1

2) Injectez l'objet parent en tant que pointeur ou référence dans chaque objet enfant. Ensuite, lorsque l'enfant a besoin de dire quelque chose à l'objet parent, il peut toujours le faire car il a une variable membre qu'il peut utiliser.

Est une option parfaitement viable. Toutes les langues modernes ont une fonction qui peut être utilisée pour faire référence à une autre langue.

DeadMG
la source
1

Il existe une approche similaire avec de légères variations mais qui présente des avantages:

Supposons que le parent A contient le composant C.

Dans le composant C, déclarez InterfaceC et maintenez-y une référence. Il s'agit de l'interface du composant avec le monde extérieur.

Le parent A implémente InterfaceC et définit sa référence dans le composant C. Le composant C voit le parent A comme InterfaceC.

L'idée est la suivante: un composant parle à l'extérieur à l'aide de son interface.

Les avantages de cette utilisation par rapport à la définition directe du parent sont les suivants:

Disons que le composant fait quelque chose et qu'il doit en informer le parent. Il appelle l'interface. Plus tard, vous décidez de modifier le parent. Le composant ne se soucie pas du tout et vous n'y apporterez aucune modification.

Dites plus tard que vous souhaitez notifier de nombreux objets d'un événement. Vous créez simplement une liste d'InterfaceC et y ajoutez des références.

Inconvénients: Une classe parent finira par implémenter de nombreuses interfaces (je pense cependant que c'est un avantage, car en regardant la déclaration de classe, je sais immédiatement qui lui parle)

Makketronix
la source
0

Notez que cela est spécifique à c #. Je ne sais pas si c ++ a quelque chose de similaire.

Si vous avez un formulaire graphique avec des boutons-poussoirs, vous avez généralement une approche différente en utilisant Event-Subscription, également connu sous le nom de Observer_pattern ou Publish – subscribe pattern .

Le bouton-poussoir ne connaît généralement pas la forme spécifique où il vit. Au lieu de cela, le bouton déclenche ou publie un événement et le formulaire reçoit une notification souscrite et peut réagir en conséquence.

A côté de gui-s, ce mécanisme peut être utilisé dans chaque relation parent-enfant

k3b
la source