8
votes

DI: Décomposition racine de composition

Racine de composition a l'air très étrange motif. Nous avons un seul très grand objet de Dieu qui sait tout sur n'importe quoi.

Quelle est la bonne façon de fractionner une racine de composition à certains modules qui encapsulera l'initialisation de sa propre partie du graphique d'objet?

Quoi de Injection de dépendance hiérarchique ?


3 commentaires

"Quelle est la bonne façon de diviser la racine de composition". Cela pourrait dépendre de la technologie que vous utilisez. Vous vous référez à la documentation angulaire. Votre question sur DI dans le contexte de l'angulaire?


@Steven, j'ai besoin de réponse conceptuelle. Vous pouvez fournir des échantillons de toute technologie, si cela vous aidera à expliquer.


Voici un article connexe: DotNetCurry.com/Patterns- Pratiques / 1285 / ...


3 Réponses :


1
votes

La racine de composition n'est pas vraiment un objet de Dieu, car il ne connaît que les parties non accaparées du graphique d'objet.

Dans la hiérarchie des composants, la racine de composition a un certain nombre d'enfants et le graphique d'objet qu'il crée matérialisée les interdépendances entre ces enfants. Un portail commercial pourrait être configuré avec une stratégie de stock, une stratégie de fonds commun de placement et une interface de courtier. Les stratégies nécessitent une interface de courtier et l'interface utilisateur du portail nécessite une ou plusieurs stratégies. La racine de composition les relie ensemble.

Maintenant, l'une de ces dépendances injectées - les stratégies de négociation ou l'interface de courtier, pourraient avoir beaucoup de fonctionnalités internes pouvant également être configurées par injection de dépendance, mais la racine de composition ne doit pas avoir aucun Connaissances durement codées sur ces internes. Au lieu de cela, la racine de composition peut être configurée avec les implémentations usine pour créer les dépendances complexes.

Les implémentations d'usine peuvent nécessiter des ressources de la racine de composition. Les usines de stratégie de négociation auront tous deux besoin de la mise en œuvre de l'interface de courtier, par exemple. Mais ils nécessitent tous les mêmes choses les mêmes de la racine de composition, de sorte que la racine de la composition n'a pas besoin de savoir sur leurs détails.

Si nécessaire, les ressources passées aux usines peuvent inclure des configurations DI pour les usines opaques à la racine de composition. Les usines peuvent ainsi créer quel que soit le graphique d'objet approprié pour implémenter l'objet qu'ils sont censés créer.

Il est important de noter que l'interface d'usine - la chose que l'application appelle à la construction de l'objet dont il a besoin - est spécifique à l'application . C'est le même sur toutes les manières configurables de satisfaire un type particulier de dépendance. Il y aura une interface tradingstrategactory ci-dessus, par exemple, avec différentes implémentations de fonds communs de placement (à l'aide du module de fonds commun de placement) et des stocks (à l'aide du module de stock).

Sauf si l'interface d'usine n'est normalisée d'une manière ou d'une autre, l'interface et les implémentations d'usine sont une partie de l'application, qui ne font pas partie des modules qui implémentent les fonctionnalités qu'elles utilisent.

occasionnellement, les exigences vont changer et un nouveau module nécessitera une ressource ou une connexion non fournie par la racine de composition. Cela nécessitera une modification de l'interface d'usine et de toutes les implémentations. Il devrait pas nécessiter des modifications apportées à d'autres modules, de sorte que ces implémentations d'usine font donc partie de l'application elle-même.

Alors, enfin ... La réponse à votre question est qu'une racine de composition importante et complexe se décompose en plusieurs implémentations d'usine de divers types.

Vous mentionnez également hiérarchique di en angulaire. Il s'agit d'un mécanisme permettant de transmettre automatiquement des ressources configurées dans les usines de composants. Je ne connais pas l'angulaire, mais je devinerais que c'est nécessaire, car Angular essaie de normaliser une seule interface d'usine pour tous les composants. Cela a pour effet de déplacer la construction du graphique d'objet elle-même dans la configuration DI dans une voie difficile à comprendre. Vous n'avez pas besoin de cela si vous programmez dans .NET et je l'éviterais dans cet environnement.


2 commentaires

Merci, mais pouvez-vous fournir des liens à lire sur cette technique de DepComposition aux usines? Son DIFCULLT doit comprendre toute l'image de votre texte.


Désolé, pas de liens. Cette réponse provient de l'expérience, pas d'apprentissage du livre. Je penserai à ce qui pourrait être difficile à comprendre, cependant. Pendant ce temps, le @steven que vous avez accroché ci-dessus est co-auteur du livre que vous référez, alors peut-être que vous obtiendrez une réponse d'or de lui.



10
votes

Je ne suis pas d'accord avec le principe qu'une racine de composition est un objet de Dieu . Bien que, conceptuellement, une racine de composition peut contenir de nombreuses lignes de code, elle n'a qu'un Responsabilité unique : composer un graphique d'objet.

Une racine de composition peut contenir de nombreuses données, et elle-même être couplée à l'ensemble de l'application, mais elle n'a conceptuellement que une méthode . Il n'a qu'une seule raison de changer, et c'est si vous souhaitez modifier la façon dont votre application est composée.

En outre, tandis que la racine de composition est couplée au reste de l'application, le code d'application ne sait rien de la racine de la composition. Ainsi, l'accouplement ne va dans une seule direction, comme il se doit, selon le Principe d'inversion de dépendance .

Un moyen de mettre, c'est qu'un objet de Dieu viole les cinq Principes solides , tandis qu'une racine de composition à tout le moins suit SRP, ISP et DIP. Je ne pense pas que le OCP ou LSP s'applique ici.

Tout ce qui dit, si une racine de composition compose une grande application, cela pourrait toujours contenir beaucoup de code. Si vous constatez que cela devient ingérable, comment devriez-vous le casser en plus petits morceaux de code?

La même manière que vous décomposez tout autre code. Cela dépend ™.

J'espère que je n'ai jamais écrit ou dit qu'une racine de composition ne peut être qu'une classe singulaire. Si je l'ai fait, j'aimerais rétracter cela. Lorsque vous écrivez des racines de composition, je divise régulièrement ses responsabilités sur plus d'une seule classe (bien que rarement plus qu'une poignée). Vous pouvez appliquer n'importe quel motif de conception ou autre technique de l'OOD que vous souhaitez pour atteindre cet objectif. La racine de la composition devient alors un façade sur votre implémentation.

Je ne peux pas donner beaucoup plus de conseils que cela, car cela dépend de l'architecture de l'application. Si vous faites une tranchée verticale, vous devez probablement structurer la racine de la composition différemment de si vous faites une superposition horizontale, etc.

Cela dit, en règle générale, je ne le trouve pas utile d'essayer de décomposer un graphique de dépendance dans les sous-arbres. Tout d'abord, car un graphique de dépendance est un graphique et non un arbre, mais aussi parce que vous devez souvent interlaisser plusieurs préoccupations indépendantes. Vous pouvez, par exemple, avoir des sous-graphiques responsables de diverses sous-parties de votre application, mais vous devez ensuite interlaisser le même mécanisme de journalisation, ou le même mécanisme de mise en cache, ou le même mécanisme de sécurité, etc. tout le graphique. Vous ne pouvez pas facilement faire cela si vous essayez de les séparer.


7 commentaires

Merci. La racine de composition a beaucoup de raisons de changer - chaque classe, répertoriée est une raison. Et ses changements très souvent. Le problème de la décomposition est tout à fait convaincu et conduit à une source de nouvelles modèles. Regardez la réponse de la Matt - il introduit quelques nouveaux concepts - usines, stratégie de stock, interface de courtier ... est-ce la composition de la composition pourtant? Ou regarder la mise en œuvre angulaire. Le problème doit être pensé pour éviter l'apparition de nouveaux anti-modèles, je pense.


"La racine de composition a beaucoup de raisons de changer - chaque classe, répertoriée est une raison." - Ceci n'est vrai que si vous appelez explicitement le constructeur de chaque classe (qui peut changer) dans votre racine de composition plutôt que de câbler les classes individuelles par elles-mêmes et laissez le conteneur les réunir. Ou mieux encore, inscrivez-vous la plupart de vos classes par convention, vous disposez donc de très peu de code de composition, puis de trouver un moyen de l'organiser, ce n'est pas une si grande préoccupation.


@ Nightowl888 Même si vous utilisez un conteneur DI, l'ajout, la suppression, le renommage et le déplacement des classes augmenteront le changement de racine de composition. Au fait, l'instanciation automatique par DI Conteneur peut ne pas être des erreurs évidentes, telles que l'utilisation d'un service non enregistré (qui ne sera découverte que d'exécution). Ainsi, l'utilisation d'un conteneur di fait également une racine de composition moins gérable.


Je sais que c'est un ancien poste, mais j'aimerais toujours entendre votre opinion sur les racines de composition pour une application côté serveur. Le problème que j'ai rencontré est que pour chaque demande, certains objets sotculiers seront créés (et forment un graphique) tandis que les objets d'application-wise restent apatrides, mais il devient compliqué de construire ce graphique par demande sans racine de composition. Est-ce que ça va d'avoir une racine de composition par demande? Ou y a-t-il une meilleure approche?


Le problème particulier que nous avons rencontré est comme ça. Nous avons une classe A et une classe B qui est à la fois sotculeuse et les deux dépendent de la classe C qui est également remarquable. Pour chaque demande RPC, nous voulons construire un A, un B et un C où A & B dépend de la même instance C, formant un graphique d'objet d'exécution. Mais si nous composons notre candidature avec trois usines AFACT-> CFACT et BFACT-> CFACFACT et appelez AVACT et BFACT pour construire A et B, nous finirons avec 2 exemples de C différent. Devrions-nous mettre en œuvre une racine de composition par demande qui construit ce graphique d'exécution?


@Jasonzang Je n'ai jamais voulu impliquer que le code racine de composition ne peut que exécuter une fois. Il peut définitivement construire des graphiques d'objet par demande. Voir par exemple blog.ploeh.dk/2014/06/03/Compile- Time-Lifetime-correspondant pour un exemple.


@Mmarkseemann Bien que je suis l'un des rares bizarches enthousiastes de C ++ di enthousiastes, j'ai peut-être besoin d'adapter votre exemple avec quelques modèles et consexpr si ou distinct créer dans plusieurs fonctions, Votre exemple a parfaitement résolu notre problème. Merci beaucoup!



2
votes

Je pense que la communauté n'est pas un accord total sur la façon dont la racine de la composition devrait ressembler.

Par exemple, vous pouvez filtrer vos objets manuellement en pratiquant Pure Dépendabilité Injection (pure di) , ou vous pouvez utiliser un conteneur d'injection de dépendance et enregistrer vos mappages manuellement, ou vous pouvez utiliser un conteneur d'injection de dépendance et créer des conventions autour des noms de classe et d'interface pour obtenir ce que l'on appelle la convention sur la configuration. Ou peut-être que vous pouvez mélanger entre ces trois approches.

Un autre exemple: vous pouvez choisir de tout câbler à l'intérieur de la racine de composition (meilleure option à mon avis), ou vous pouvez choisir d'avoir une "racine de composition de petite composition" pour chaque composant (par exemple une bibliothèque de classe dans .NET) dans le système, puis câbler ces racines dans la "rotation de la composition principale".

Selon l'approche que vous choisissez, la connaissance de la racine de composition variera.

Par exemple, si vous utilisez Pure DI et que vous avez une racine de composition unique, la racine de la composition devra en savoir beaucoup sur l'application.

D'autre part, si vous suivez la convention sur une approche de configuration, une partie des connaissances sur lesquelles les classes doivent être mappées sur les interfaces seront distribuées partout sur la place (nom de classe et d'interface, etc.). < / p>

Basé sur mon expérience, Pure DI + ayant une seule racine de composition est la meilleure option pour toute application suffisante. Cela vous donne un endroit central pour comprendre l'application et rend votre Racine de composition navigable .

Cela signifie que la racine de la composition a une très grande responsabilité. Je ne suis pas sûr de pouvoir l'appeler un objet de Dieu. Je pense que le contexte où ce terme a été inventé est différent.

Je ne dirais pas que la racine de la composition "aussi" beaucoup. Bien que cela fasse beaucoup, je pense que c'est censé. Si vous êtes comme moi, vous voulez un seul endroit où vous pouvez naviguer et comprendre votre application.

Pour décomposer votre racine de composition, vous pouvez la diviser en plusieurs méthodes. Je crée parfois une classe partielle en C # (une seule classe dans plusieurs fichiers) et mettre des méthodes associées dans le même fichier.

Bien que le graphique d'application soit un graphique, il sera beaucoup plus facile de se décomposer s'il s'agissait d'un arbre. Je fais de mon mieux pour faire le graphique de type graphique. Si deux objets nécessitent une dépendance similaire, j'essaie de leur donner des instances différentes de ces dépendances pour maintenir la structure de type arborescente du graphique.

J'ai écrit un article sur ce sujet, vous pouvez le lire ici .

ici est également un autre article qui fournit une perspective sur la responsabilité de la racine de composition.


5 commentaires

Merci, vous avez énuméré toutes les choses évidentes, mais ne touchez pas le noyau du problème. Si nous franchissons la racine de la composition, des députés seront courantes, certaines spécifiques aux modules (ou à l'arbre de modules?). La question principale est de savoir comment organiser l'interaction entre des parties communes et spécifiques, le rendre prévisible, maintenu et facile à gérer.


@Pavel, les seules choses à partager sont des objets d'état partagés et des ressources partagées (par exemple, des fichiers ouverts et verrouillés). Pour ces objets, vous les créez au dernier endroit possible, puis transmettez-les sous forme de paramètres dans les méthodes racines de la composition. Tous les autres objets apatrides n'ont pas besoin d'être partagés. J'explique cela en détail dans la section "Partage de code contre ressource / partage de l'état" de la Article lié .


@Pavel, à la manière, cela a beaucoup de sens pour maximiser le nombre d'éléments «objets» apatrides et minimiser le nombre d'éléments "objets" d'état. Cela vous aidera beaucoup avec le maintien de votre racine de la composition (entre autres). Voici un article associé: DotNetCurry.com/Patterns- Pratiques / 1367 / ...


Sa bonne idée, mais on dirait que c'est du monde idéal. Avez-vous une expiration de l'utilisation de cela dans de vraies grandes applications?


@Pavel, oui. Les articles que j'écris sont basés sur mon expérience de construction de grandes applications qui sont en maintenance depuis des années.