Quels sont les inconvénients de l'utilisation de l'injection de dépendance? [fermé]

336

J'essaie d'introduire DI en tant que modèle ici au travail et l'un de nos principaux développeurs aimerait savoir: quels sont, le cas échéant, les inconvénients de l'utilisation du modèle d'injection de dépendance?

Notez que je cherche ici une liste - si possible - exhaustive, pas une discussion subjective sur le sujet.


Clarification : je parle du modèle d' injection de dépendances (voir cet article de Martin Fowler), pas d' un cadre spécifique, qu'il soit basé sur XML (comme Spring) ou basé sur du code (tel que Guice), ou "auto-rolled" .


Edit : Quelques grandes discussions / diatribes / débats en cours / r / programmation ici.

Epaga
la source
Ce pourrait être une bonne idée de spécifier si nous sommes censés discuter de la DI elle-même ou d'un type spécifique d'outil qui la prend en charge (basé sur XML ou non).
Jesse Millikan
5
la différence est que je ne recherche PAS de réponses qui ne s'appliquent qu'à des cadres spécifiques, tels que "XML sucks". :) Je cherche des réponses qui s'appliquent au concept de DI tel que décrit par Fowler ici: martinfowler.com/articles/injection.html
Epaga
3
Je ne vois aucun inconvénient à DI en général (constructeur, setter ou méthode). Il dissocie et simplifie sans frais généraux.
Kissaki
2
@kissaki bien à part les trucs d'injection de setter. Mieux vaut fabriquer des objets utilisables lors de la construction. Si vous ne me croyez pas, essayez simplement d'ajouter un autre collaborateur à un objet avec une injection 'setter', vous obtiendrez un NPE à coup sûr ....
time4tea

Réponses:

209

Quelques points:

  • DI augmente la complexité, généralement en augmentant le nombre de classes car les responsabilités sont plus séparées, ce qui n'est pas toujours bénéfique
  • Votre code sera (quelque peu) couplé au framework d'injection de dépendances que vous utilisez (ou plus généralement comment vous décidez d'implémenter le modèle DI)
  • Les conteneurs DI ou les approches qui effectuent une résolution de type entraînent généralement une légère pénalité d'exécution (très négligeable, mais c'est là)

Généralement, l'avantage du découplage rend chaque tâche plus simple à lire et à comprendre, mais augmente la complexité de l'orchestration des tâches les plus complexes.

Håvard S
la source
72
- La séparation des classes réduit la complexité. De nombreuses classes ne rendent pas une application complexe. - Vous ne devriez avoir qu'une dépendance à votre infrastructure DI à la racine de l'application.
Robert
91
Nous sommes des humains; notre mémoire à court terme est limitée et ne peut pas gérer plusieurs <xxx> simultanément. C'est la même chose pour les classes que pour les méthodes, les fonctions, les fichiers ou toute construction que vous utilisez pour développer votre programme.
Håvard S
45
@Havard S: Oui, mais 5 classes vraiment complexes ne sont pas plus simples que 15 classes simples. Le moyen de réduire la complexité consiste à réduire l'ensemble de travail requis par le programmeur travaillant sur le code - les classes complexes et fortement interdépendantes ne font pas cela.
kyoryu
35
@kyoryu Je pense que nous sommes tous d'accord là-dessus. Gardez à l'esprit que la complexité n'est pas la même chose que le couplage . La diminution du couplage peut augmenter la complexité. Je ne dis vraiment pas que la DI est une mauvaise chose car elle augmente le nombre de classes, je souligne les inconvénients potentiels qui y sont associés. :)
Håvard S
96
+1 pour "DI augmente la complexité" C'est vrai en DI et au-delà. (Presque) chaque fois que nous augmentons la flexibilité, nous allons augmenter la complexité. Tout est question d'équilibre, qui commence par connaître les avantages et les inconvénients. Quand les gens disent: «il n'y a pas d'inconvénients», c'est un indicateur certain qu'ils n'ont pas encore complètement compris la chose.
Don Branson
183

Le même problème de base que vous rencontrez souvent avec la programmation orientée objet, les règles de style et à peu près tout le reste. Il est possible - très courant, en fait - de faire trop d'abstraction, d'ajouter trop d'indirection et d'appliquer généralement de bonnes techniques de manière excessive et aux mauvais endroits.

Chaque modèle ou autre construction que vous appliquez apporte de la complexité. L'abstraction et l'indirection dispersent les informations, déplaçant parfois les détails non pertinents hors du chemin, mais rendant également parfois plus difficile de comprendre exactement ce qui se passe. Chaque règle que vous appliquez apporte de la rigidité, excluant les options qui pourraient être la meilleure approche.

Le but est d'écrire du code qui fait le travail et qui est robuste, lisible et maintenable. Vous êtes un développeur de logiciels - pas un constructeur de tours d'ivoire.

Liens pertinents

http://thedailywtf.com/Articles/The_Inner-Platform_Effect.aspx

http://www.joelonsoftware.com/articles/fog0000000018.html


La forme d'injection de dépendance la plus simple (ne riez pas) est probablement un paramètre. Le code dépendant dépend des données, et ces données sont injectées par le biais du passage du paramètre.

Oui, c'est idiot et cela ne traite pas du point d'injection de dépendance orienté objet, mais un programmeur fonctionnel vous dira que (si vous avez des fonctions de première classe), c'est le seul type d'injection de dépendance dont vous avez besoin. Le point ici est de prendre un exemple trivial et de montrer les problèmes potentiels.

Prenons cette simple fonction traditionnelle - la syntaxe C ++ n'est pas significative ici, mais je dois l'épeler en quelque sorte ...

void Say_Hello_World ()
{
  std::cout << "Hello World" << std::endl;
}

J'ai une dépendance que je veux extraire et injecter - le texte "Hello World". Assez facile...

void Say_Something (const char *p_text)
{
  std::cout << p_text << std::endl;
}

Comment est-ce plus rigide que l'original? Et si je décide que la sortie doit être unicode. Je veux probablement passer de std :: cout à std :: wcout. Mais cela signifie que mes cordes doivent alors être de wchar_t, pas de char. Soit chaque appelant doit être modifié, soit (plus raisonnablement), l'ancienne implémentation est remplacée par un adaptateur qui traduit la chaîne et appelle la nouvelle implémentation.

Il s'agit là de travaux de maintenance qui ne seraient pas nécessaires si nous conservions l'original.

Et si cela semble trivial, jetez un œil à cette fonction du monde réel de l'API Win32 ...

http://msdn.microsoft.com/en-us/library/ms632680%28v=vs.85%29.aspx

C'est 12 "dépendances" à gérer. Par exemple, si les résolutions d'écran deviennent vraiment énormes, nous aurons peut-être besoin de valeurs de coordonnées 64 bits - et d'une autre version de CreateWindowEx. Et oui, il y a déjà une ancienne version qui traîne, qui est probablement mappée à la nouvelle version dans les coulisses ...

http://msdn.microsoft.com/en-us/library/ms632679%28v=vs.85%29.aspx

Ces "dépendances" ne sont pas seulement un problème pour le développeur d'origine - tous ceux qui utilisent cette interface doivent rechercher quelles sont les dépendances, comment elles sont spécifiées et ce qu'elles signifient, et déterminer ce qu'il faut faire pour leur application. C'est là que les mots «valeurs par défaut sensibles» peuvent rendre la vie beaucoup plus simple.

L'injection de dépendances orientée objet n'est pas différente en principe. L'écriture d'une classe est une surcharge, à la fois dans le texte du code source et dans le temps du développeur, et si cette classe est écrite pour fournir des dépendances selon certaines spécifications d'objets dépendants, alors l'objet dépendant est verrouillé pour prendre en charge cette interface, même s'il y a un besoin pour remplacer l'implémentation de cet objet.

Rien de tout cela ne doit être interprété comme prétendant que l'injection de dépendance est mauvaise - loin de là. Mais toute bonne technique peut être appliquée de manière excessive et au mauvais endroit. Tout comme toutes les chaînes n'ont pas besoin d'être extraites et transformées en paramètre, tous les comportements de bas niveau ne doivent pas être extraits des objets de haut niveau et transformés en une dépendance injectable.

Steve314
la source
3
L'injection de dépendance ne présente aucun de ces inconvénients. Il ne fait que modifier la façon dont vous passez les dépendances aux objets, ce qui n'ajoute ni complexité ni rigidité. Bien au contraire.
Kissaki
24
@Kissaki - oui, cela change la façon dont vous passez vos dépendances. Et vous devez écrire du code pour gérer ce passage de dépendances. Et avant de faire cela, vous devez déterminer les dépendances à passer, les définir d'une manière qui a du sens pour quelqu'un qui n'a pas codé le code dépendant, etc. Vous pouvez éviter l'impact au moment de l'exécution (par exemple en utilisant des paramètres de stratégie dans des modèles en C ++), mais c'est toujours du code que vous devez écrire et maintenir. Si c'est justifié, c'est un très petit prix pour une grosse récompense, mais si vous prétendez qu'il n'y a pas de prix à payer, cela signifie simplement que vous paierez ce prix quand il n'y a pas de gain.
Steve314
6
@Kissaki - en ce qui concerne l'inflexibilité, une fois que vous avez spécifié vos dépendances, vous êtes enfermé dans cela - d'autres personnes ont des dépendances écrites à injecter selon cette spécification. Si vous avez besoin d'une nouvelle implémentation pour le code dépendant qui a besoin de dépendances légèrement différentes - difficile, vous êtes verrouillé. Il est temps de commencer à écrire des adaptateurs pour ces dépendances à la place (et un peu plus de surcharge et une autre couche d'abstraction).
Steve314
Je ne suis pas sûr de comprendre cet exemple, si vous envisagez de surcharger. Pour sortir le littéral "Hello World", nous utilisons la signature (). Pour sortir une chaîne de 8 bits, utilisez la signature (char). Pour sortir unicode, la surcharge (wchar_t). Évidemment, dans ce cas, (wchar_t) est celui que les autres appellent dans les coulisses. Pas beaucoup de réécriture de code nécessaire. Suis-je en train de manquer quelque chose?
ingrédient_15939
2
@ ingrédient_15939 - oui, c'est un exemple de jouet simplifié. Si vous faisiez vraiment cela, vous utiliseriez simplement la surcharge plutôt qu'un adaptateur car le coût de duplication du code est inférieur au coût de l'adaptateur. Mais notez que la duplication de code est généralement une mauvaise chose, et l'utilisation d'adaptateurs pour éviter la duplication de code est une autre bonne technique (qui peut parfois être trop utilisée et aux mauvais endroits).
Steve314
77

Voici ma propre réaction initiale: Fondamentalement, les mêmes inconvénients de tout modèle.

  • il faut du temps pour apprendre
  • si elle est mal comprise, elle peut entraîner plus de mal que de bien
  • si elle est poussée à l'extrême, cela peut être plus de travail que ne justifierait l'avantage
Epaga
la source
2
Pourquoi faut-il du temps pour apprendre? Je pensais que cela rend les choses simples par rapport à ejb.
fastcodejava
8
Cela semble ondulé si vous ne voulez pas de discussion subjective. Je suggère de construire (collectivement, si nécessaire) un exemple réaliste d'examen. Construire un échantillon sans DI serait un bon point de départ. Ensuite, nous pourrions le contester par rapport aux modifications des exigences et examiner l'impact. (C'est extrêmement pratique pour moi de suggérer, en passant, alors que je vais me coucher.)
Jesse Millikan
4
Cela a vraiment besoin de quelques exemples pour le sauvegarder, et après avoir enseigné à des dizaines de personnes DI, je peux vous dire que c'est vraiment un concept très simple à apprendre et à commencer à mettre en pratique.
chillitom
4
Je ne considère pas vraiment DI comme un modèle. Il (couplé à l'IoC) est vraiment plus un modèle de programmation - si vous les suivez pleinement, votre code ressemble beaucoup plus à un acteur qu'à un OO pseudo-procédural "typique".
kyoryu
1
+1 J'utilise Symfony 2, la DI est partout dans le code utilisateur, c'est juste en l'utilisant.
MGP
45

Le plus gros "inconvénient" de l'inversion de contrôle (pas tout à fait DI, mais assez proche) est qu'il tend à supprimer le fait d'avoir un seul point pour regarder un aperçu d'un algorithme. C'est essentiellement ce qui se produit lorsque vous avez découplé du code - la possibilité de regarder en un seul endroit est un artefact de couplage étroit.

kyoryu
la source
2
Mais cet «inconvénient» est certainement dû à la nature du problème que nous résolvons, le découpler afin que nous puissions facilement changer la mise en œuvre signifie qu'il n'y a pas un seul endroit à regarder, et quelle pertinence est-il capable de le regarder? Le seul cas que je puisse penser est celui du débogage et l'environnement de débogage devrait pouvoir entrer dans l'implémentation.
vickirk
3
C'est pourquoi le mot "inconvénient" était entre guillemets :) Un couplage lâche et une forte encapsulation empêchent "un seul endroit pour tout voir", un peu par définition. Voir mes commentaires sur la réponse de Havard S, si vous avez le sentiment que je suis contre DI / IoC.
kyoryu
1
Je suis d'accord (je vous ai même voté). Je faisais juste le point.
vickirk
1
@vickirk: L'inconvénient, je suppose, c'est qu'il est difficile pour un humain de comprendre. Le découplage des choses augmente la complexité dans le sens où il est plus difficile à comprendre pour les humains et prend plus de temps à comprendre pleinement.
Bjarke Freund-Hansen
2
Au contraire, l'un des avantages de DI est que vous pouvez regarder le constructeur d'une classe individuelle et déterminer instantanément de quelles classes il dépend (c'est-à-dire quelles classes doivent faire son travail). C'est beaucoup plus difficile pour les autres codes, car le code peut créer des classes à volonté.
Contango
42

Je ne pense pas qu'une telle liste existe, mais essayez de lire ces articles:

Gabriel Ščerbák
la source
7
Je suis curieux, quand quelqu'un demande wonsides, pourquoi les réponses dans un esprit "il n'y en a pas" sont votées et celles qui contiennent des informations liées à la question non?
Gabriel Ščerbák
12
-1 parce que je ne recherche pas de collection de liens, je recherche des réponses réelles: que diriez-vous de résumer les points de chaque article? Ensuite, je voterais à la place. Notez que les articles eux-mêmes sont assez intéressants, bien qu'il semble que les deux premiers soient en fait des éléments négatifs de DI?
Epaga
4
Je me demande si la réponse la plus positive a également été rejetée, car elle ne répond pas du tout à la question à mon humble avis :) Je sais bien ce que vous avez demandé et ce que j'ai répondu, je ne demande pas de votes positifs, je suis juste confus pourquoi mon la réponse n'aide pas tout en disant apparemment que le DI est que c'est trop cool, ça aide beaucoup.
Gabriel Ščerbák
41

J'utilise Guice (framework Java DI) de façon intensive depuis 6 mois. Bien que dans l'ensemble, je pense que c'est génial (surtout du point de vue des tests), il y a certains inconvénients. Notamment:

  • Le code peut devenir plus difficile à comprendre. L'injection de dépendance peut être utilisée de manière très ... créative .... Par exemple, je suis juste tombé sur un code qui utilisait une annotation personnalisée pour injecter un certain IOStreams (par exemple: @ Server1Stream, @ Server2Stream). Bien que cela fonctionne, et j'avoue avoir une certaine élégance, cela fait de la compréhension des injections de Guice une condition préalable à la compréhension du code.
  • Courbe d'apprentissage plus élevée lors de l'apprentissage du projet. Ceci est lié au point 1. Pour comprendre le fonctionnement d'un projet qui utilise l'injection de dépendance, vous devez comprendre à la fois le modèle d'injection de dépendance et le cadre spécifique. Quand j'ai commencé mon travail actuel, j'ai passé pas mal d'heures confuses à essayer de comprendre ce que faisait Guice dans les coulisses.
  • Les constructeurs deviennent grands. Bien que cela puisse être largement résolu avec un constructeur par défaut ou une usine.
  • Les erreurs peuvent être masquées. Mon exemple le plus récent en est que j'ai eu une collision avec 2 noms de drapeau. Guice a avalé l'erreur en silence et l'un de mes drapeaux n'a pas été initialisé.
  • Les erreurs sont transmises à l'exécution. Si vous configurez votre module Guice de manière incorrecte (référence circulaire, mauvaise liaison, ...) la plupart des erreurs ne sont pas découvertes lors de la compilation. Au lieu de cela, les erreurs sont exposées lorsque le programme est réellement exécuté.

Maintenant que je me suis plaint. Permettez-moi de dire que je continuerai à utiliser (volontiers) Guice dans mon projet actuel et probablement mon prochain. L'injection de dépendance est un modèle génial et incroyablement puissant. Mais cela peut certainement être déroutant et vous passerez presque certainement du temps à maudire le cadre d'injection de dépendance que vous choisissez.

De plus, je suis d'accord avec d'autres affiches sur le fait que l'injection de dépendance peut être surutilisée.

Andy Peck
la source
14
Je ne comprends pas pourquoi les gens ne font pas beaucoup plus de "Les erreurs sont poussées au moment de l'exécution" pour moi, c'est un casse-tête, les erreurs de frappe statique et de compilation sont le plus grand cadeau offert aux développeurs, je ne lancerais pas les éloigner pour rien
Richard Tingle
2
@RichardTingle, IMO pendant le démarrage de l'application Les modules DI seront initialisés en premier afin que toute mauvaise configuration dans le module soit visible dès le démarrage de l'application plutôt qu'après quelques jours ou heures. Il est également possible de charger les modules de manière incrémentielle, mais si nous respectons l'esprit de DI en limitant le chargement des modules avant d'initialiser la logique d'application, nous pouvons isoler avec succès les mauvaises liaisons au démarrage de l'application. Mais si nous le configurons comme modèle anti localisateur de service, ces mauvaises liaisons seront sûrement une surprise.
k4vin
@RichardTingle Je comprends qu'il ne sera jamais similaire au filet de sécurité fourni par le compilateur mais DI en tant qu'outil utilisé correctement comme indiqué dans la boîte, ces erreurs d'exécution sont limitées à l'initialisation de l'application. Ensuite, nous pouvons considérer l'initialisation de l'application comme une sorte de phase de compilation pour les modules DI. D'après mon expérience, la plupart du temps, si l'application démarre, il n'y aura pas de mauvaises liaisons ou de références incorrectes. PS - J'utilise NInject avec C #
k4vin
@RichardTingle - Je suis d'accord avec vous, mais c'est un compromis afin d'obtenir du code librement couplé, donc testable. Et comme l'a dit k4vin, des dépendances manquantes sont trouvées à l'initialisation, et l'utilisation d'interfaces aidera toujours à la compilation des erreurs de temps.
Andrei Epure
1
J'ajouterais «Difficile de garder le code propre» dans votre liste. Vous ne savez jamais si vous pouvez supprimer un enregistrement avant de tester de manière approfondie le code sans lui.
fernacolo
24

Le code sans DI présente le risque bien connu de s'emmêler dans le code Spaghetti - certains symptômes sont que les classes et les méthodes sont trop grandes, font trop et ne peuvent pas être facilement modifiées, décomposées, refactorisées ou testées.

Le code avec DI utilisé beaucoup peut être du code Ravioli où chaque petite classe est comme une pépite de ravioli individuelle - cela fait une petite chose et le principe de responsabilité unique est respecté, ce qui est bien. Mais en regardant les cours par eux-mêmes, il est difficile de voir ce que fait le système dans son ensemble, car cela dépend de la façon dont toutes ces nombreuses petites pièces s'assemblent, ce qui est difficile à voir. Cela ressemble à un gros tas de petites choses.

En évitant la complexité spaghetti de gros morceaux de code couplé au sein d'une grande classe, vous courez le risque d'un autre type de complexité, où il y a beaucoup de petites classes simples et les interactions entre elles sont complexes.

Je ne pense pas que ce soit un inconvénient fatal - DI est toujours très utile. Un certain degré de style ravioli avec de petites classes qui ne font qu'une chose est probablement bon. Même en excès, je ne pense pas que ce soit mauvais comme code de spaghetti. Mais être conscient qu'il peut être poussé trop loin est la première étape pour l'éviter. Suivez les liens pour discuter de la façon de l'éviter.

Anthony
la source
1
Oui, j'aime ce terme "code ravioli"; Je l'exprime plus longuement quand je parle de l'inconvénient de DI. Pourtant, je ne peux pas imaginer développer un véritable framework ou application en Java sans DI.
Howard M. Lewis Ship
13

Si vous avez une solution maison, les dépendances sont bien visibles dans le constructeur. Ou peut-être comme des paramètres de méthode qui ne sont pas trop difficiles à repérer. Bien que les dépendances gérées par le framework, si elles sont poussées à l'extrême, peuvent commencer à apparaître comme par magie.

Cependant, avoir trop de dépendances dans trop de classes est un signe clair que votre structure de classe est foutue. Donc, d'une certaine manière, l'injection de dépendance (développée à la maison ou gérée par le framework) peut aider à mettre en évidence des problèmes de conception flagrants qui pourraient autrement être cachés dans le noir.


Pour mieux illustrer le deuxième point, voici un extrait de cet article ( source originale ) qui, à mon avis, est le problème fondamental de la construction de tout système, pas seulement des systèmes informatiques.

Supposons que vous souhaitiez concevoir un campus universitaire. Vous devez déléguer une partie de la conception aux étudiants et aux professeurs, sinon le bâtiment de physique ne fonctionnera pas bien pour les gens de physique. Aucun architecte n'en sait assez sur ce dont les gens ont besoin pour tout faire eux-mêmes. Mais vous ne pouvez pas déléguer la conception de chaque pièce à ses occupants, car vous obtiendrez alors un tas de décombres géants.

Comment pouvez-vous répartir la responsabilité de la conception à tous les niveaux d'une grande hiérarchie, tout en maintenant la cohérence et l'harmonie de la conception globale? C'est le problème de conception architecturale qu'Alexander essaie de résoudre, mais c'est aussi un problème fondamental du développement de systèmes informatiques.

DI résout-il ce problème? Non . Mais cela vous aide à voir clairement si vous essayez de déléguer la responsabilité de la conception de chaque pièce à ses occupants.

Anurag
la source
13

Une chose qui me fait me tortiller un peu avec DI est l'hypothèse que tous les objets injectés sont bon marché pour instancier et ne produisent aucun effet secondaire -OU- la dépendance est utilisée si fréquemment qu'elle l'emporte sur tout coût d'instanciation associé.

Lorsque cela peut être significatif, c'est lorsqu'une dépendance n'est pas fréquemment utilisée au sein d'une classe consommatrice; comme quelque chose comme un IExceptionLogHandlerService. De toute évidence, un service comme celui-ci est invoqué (espérons-le :)) rarement dans la classe - sans doute uniquement sur les exceptions devant être enregistrées; mais le modèle d'injection de constructeur canonique ...

Public Class MyClass
    Private ReadOnly mExLogHandlerService As IExceptionLogHandlerService

    Public Sub New(exLogHandlerService As IExceptionLogHandlerService)
        Me.mExLogHandlerService = exLogHandlerService
    End Sub

     ...
End Class

... exige qu'une instance "en direct" de ce service soit fournie, sacrément les coûts / effets secondaires nécessaires pour y arriver. Non pas que ce serait probablement le cas, mais que se passe-t-il si la construction de cette instance de dépendance implique un accès au service / à la base de données, ou des recherches de fichiers de configuration, ou verrouille une ressource jusqu'à sa suppression? Si ce service était plutôt construit selon les besoins, situé dans le service ou généré en usine (tous ayant des problèmes qui leur sont propres), vous ne prendriez le coût de construction que lorsque cela serait nécessaire.

Maintenant, c'est un principe de conception de logiciel généralement accepté que la construction d'un objet est bon marché et ne produit pas d' effets secondaires. Et bien que ce soit une bonne idée, ce n'est pas toujours le cas. Cependant, l'utilisation d'une injection de constructeur typique exige essentiellement que ce soit le cas. Cela signifie que lorsque vous créez une implémentation d'une dépendance, vous devez la concevoir avec DI à l'esprit. Vous auriez peut-être rendu la construction d'objets plus coûteuse pour obtenir des avantages ailleurs, mais si cette implémentation devait être injectée, elle vous obligerait probablement à reconsidérer cette conception.

Soit dit en passant, certaines techniques peuvent atténuer ce problème exact en permettant le chargement différé des dépendances injectées, par exemple en fournissant à une classe une Lazy<IService>instance comme dépendance. Cela changerait les constructeurs de vos objets dépendants et rendrait alors encore plus conscients des détails d'implémentation tels que les dépenses de construction d'objets, ce qui n'est sans doute pas souhaitable non plus.

ckittel
la source
1
Je comprends ce que vous dites, mais je ne pense pas qu'il soit juste de dire "lorsque vous créez une implémentation d'une dépendance, vous devez la concevoir en ayant DI à l'esprit" - je pense qu'une déclaration plus précise serait "DI fonctionne mieux lorsqu'il est utilisé avec des classes qui sont mises en œuvre avec les meilleures pratiques concernant le coût d'instanciation et un manque d'effets secondaires ou de modes de défaillance pendant la construction ". Dans le pire des cas, vous pouvez toujours injecter une implémentation de proxy paresseux qui diffère l'allocation de l'objet réel jusqu'à la première utilisation.
Jolly Roger
1
Tout conteneur IoC moderne vous permettra de spécifier la durée de vie d'un objet pour un type / interface abstrait spécifique (toujours unique, singleton, de portée http, etc.). Vous pouvez également fournir une méthode / délégué d'usine pour l'instancier paresseusement en utilisant Func <T> ou Lazy <T>.
Dmitry S.
Repérez. L'instanciation des dépendances coûte inutilement de la mémoire. J'utilise normalement le "chargement paresseux" avec des getters qui n'instancient une classe que lorsqu'ils sont appelés. Vous pouvez fournir par défaut un constructeur sans paramètres ainsi que des constructeurs ultérieurs qui acceptent les dépendances instanciées. Dans le premier cas, une classe implémentant IErrorLogger, par exemple, n'est instanciée qu'avec this.errorLogger.WriteError(ex)lorsqu'une erreur se produit dans une instruction try / catch.
John Bonfardeci
12

L'illusion que vous avez découplé votre code simplement en implémentant l'injection de dépendance sans réellement le découpler. Je pense que c'est la chose la plus dangereuse avec DI.

Joey Guerra
la source
11

Il s'agit plus d'un nitpick. Mais l'un des inconvénients de l'injection de dépendances est qu'il est un peu plus difficile pour les outils de développement de raisonner et de naviguer dans le code.

Plus précisément, si vous contrôlez-cliquez / commande-cliquez sur une invocation de méthode dans le code, cela vous amènera à la déclaration de méthode sur une interface au lieu de l'implémentation concrète.

C'est vraiment plus un inconvénient du code à couplage lâche (code conçu par l'interface), et s'applique même si vous n'utilisez pas l'injection de dépendance (c'est-à-dire, même si vous utilisez simplement des usines). Mais l'avènement de l'injection de dépendances est ce qui a vraiment encouragé le code faiblement couplé aux masses, alors j'ai pensé le mentionner.

En outre, les avantages du code faiblement couplé l'emportent largement sur cela, donc je l'appelle un nitpick. Bien que j'aie travaillé assez longtemps pour savoir que c'est le genre de refoulement que vous obtiendrez si vous essayez d'introduire l'injection de dépendance.

En fait, je me risquerais à deviner que pour chaque "inconvénient" que vous pouvez trouver pour l'injection de dépendance, vous trouverez de nombreux avantages qui l'emportent de loin.

Jack Leow
la source
5
Ctrl-shift-B avec resharper vous prend la mise en œuvre
adrianm
1
Mais là encore, ne serions-nous pas (dans un monde idéal) au moins 2 implémentations de tout, c'est-à-dire au moins une implémentation réelle et une maquette pour les tests unitaires ;-)
vickirk
1
Dans Eclipse, si vous maintenez la touche Ctrl enfoncée et survolez le code d'appel de la méthode, un menu vous ouvrira la déclaration ou l'implémentation.
Sam Hasler
Si vous êtes à la définition de l'interface, mettez en surbrillance le nom de la méthode et appuyez sur Ctrl + T pour afficher la hiérarchie de types pour cette méthode - vous verrez chaque implémenteur de cette méthode dans une arborescence de hiérarchies de types.
qualidafial
10

L' injection de dépendances basée sur le constructeur (sans l'aide de "frameworks" magiques) est un moyen propre et avantageux de structurer le code OO. Dans les meilleures bases de code que j'ai vues, au fil des années passées avec d'autres anciens collègues de Martin Fowler, j'ai commencé à remarquer que la plupart des bonnes classes écrites de cette façon finissent par avoir une seule doSomethingméthode.

L'inconvénient majeur, alors, est qu'une fois que vous vous rendez compte que tout n'est qu'une manière OO fastidieuse d'écrire des fermetures sous forme de classes afin d'obtenir les avantages de la programmation fonctionnelle, votre motivation à écrire du code OO peut rapidement s'évaporer.

sanityinc
la source
1
Le problème avec les constructeurs est que vous devrez toujours ajouter plus d'arguments constructeurs ...
Miguel Ping
1
Haha. Si vous en faites trop, vous verrez bientôt que votre code est nul et vous le refactoriserez. De plus, ctrl-f6 n'est pas si difficile.
time4tea
Et bien sûr, l'inverse est également vrai: tout cela n'est qu'une manière fonctionnelle et fastidieuse d'écrire des cours en tant que fermetures afin de bénéficier des avantages de la programmation OO
CurtainDog
Il y a plusieurs années, j'ai construit un système d'objets utilisant des fermetures en Perl, donc je comprends ce que vous dites, mais l'encapsulation de données n'est pas un avantage unique de la programmation OO, donc on ne sait pas quelles sont ces fonctionnalités bénéfiques d'OO qui seraient comparables kludgy et long à obtenir dans un langage fonctionnel.
sanityinc
9

Je trouve que l'injection de constructeur peut conduire à de gros constructeurs moches (et je l'utilise dans toute ma base de code - peut-être que mes objets sont trop granulaires?). De plus, parfois avec l'injection de constructeur, je me retrouve avec des dépendances circulaires horribles (bien que ce soit très rare), vous pouvez donc vous retrouver devant avoir une sorte de cycle de vie d'état prêt avec plusieurs cycles d'injection de dépendance dans un système plus complexe.

Cependant, je préfère l'injection de construction à l'injection de setter car une fois que mon objet est construit, je sais sans aucun doute dans quel état il se trouve, que ce soit dans un environnement de test unitaire ou chargé dans un conteneur IOC. Ce qui, d'une manière détournée, dit ce que je pense être le principal inconvénient de l'injection de setter.

(En tant que sidenote, je trouve tout le sujet assez "religieux", mais votre kilométrage variera en fonction du niveau de fanatisme technique de votre équipe de développement!)

James B
la source
8
Si vous avez de gros constructeurs moches, peut-être que vos classes sont trop grandes et que vous avez juste trop de dépendances?
Robert
4
C'est certainement une possibilité que je suis prêt à envisager! ... Malheureusement, je n'ai pas d'équipe avec laquelle je peux faire des évaluations par les pairs. les violons jouent doucement en arrière-plan, puis l'écran s'estompe au noir ...
James B
1
Comme Mark Seeman triste ci-dessus "Cela peut être dangereux pour votre carrière" ...
Robert
Je n'avais aucune idée des narrations de fond pourraient être si expressives ici: P
Anurag
@Robert J'essaie de trouver la citation, où a-t-il dit cela ci-dessus?
liang
8

Si vous utilisez DI sans conteneur IOC, le plus gros inconvénient est que vous voyez rapidement combien de dépendances votre code a réellement et comment tout est étroitement couplé. ("Mais je pensais que c'était un bon design!") La progression naturelle est de se diriger vers un conteneur IOC qui peut prendre un peu de temps pour apprendre et mettre en œuvre (pas aussi mauvais que la courbe d'apprentissage WPF, mais ce n'est pas gratuit Soit). Le dernier inconvénient est que certains développeurs commenceront à écrire des tests unitaires honnêtes et bons et il leur faudra du temps pour le comprendre. Les développeurs qui pouvaient auparavant lancer quelque chose en une demi-journée passeront soudainement deux jours à essayer de se moquer de toutes leurs dépendances.

Semblable à la réponse de Mark Seemann, l'essentiel est que vous passez du temps à devenir un meilleur développeur plutôt que de pirater des morceaux de code ensemble et de le lancer à la porte / en production. Quelle serait votre entreprise plutôt? Vous seul pouvez y répondre.

Mike Post
la source
Bon point. C'est de mauvaise qualité / livraison rapide vs bonne qualité / livraison lente. Le mauvais côté? DI n'est pas magique, s'attendre à ce que ce soit le revers.
liang
5

DI est une technique ou un modèle et n'est lié à aucun cadre. Vous pouvez câbler vos dépendances manuellement. DI vous aide avec SR (responsabilité unique) et SoC (séparation des préoccupations). DI conduit à une meilleure conception. De mon point de vue et de mon expérience, il n'y a pas d'inconvénients . Comme avec tout autre modèle, vous pouvez vous tromper ou en abuser (mais ce qui est assez difficile dans le cas de DI).

Si vous introduisez DI comme principe dans une application héritée, en utilisant un framework - la plus grosse erreur que vous puissiez faire est de l'utiliser à mauvais escient en tant que Service-Locater. DI + Framework lui-même est génial et a juste amélioré les choses partout où je l'ai vu! Du point de vue organisationnel, il y a les problèmes communs à chaque nouveau processus, technique, modèle, ...:

  • Vous devez former votre équipe
  • Vous devez changer votre application (qui inclut les risques)

En général, vous devez investir du temps et de l'argent , à côté de cela, il n'y a pas d'inconvénients, vraiment!

Robert
la source
3

Lisibilité du code. Vous ne pourrez pas facilement comprendre le flux de code car les dépendances sont masquées dans les fichiers XML.

Rahul
la source
9
Vous n'avez pas besoin d'utiliser des fichiers de configuration XML pour l'injection de dépendances - choisissez un conteneur DI qui prend en charge la configuration dans le code.
Håvard S
8
L'injection de dépendances n'implique pas nécessairement les fichiers XML. Il semble que ce soit encore le cas en Java, mais en .NET, nous avons abandonné ce couplage il y a des années.
Mark Seemann
6
Ou n'utilisez pas du tout de conteneur DI.
Jesse Millikan
si vous savez que le code utilise DI, vous pouvez facilement supposer qui définit les dépendances.
Bozho
En Java, Google's Guice n'a pas besoin de fichiers XML par exemple.
Epaga
2

Deux choses:

  • Ils nécessitent un support d'outil supplémentaire pour vérifier que la configuration est valide.

Par exemple, IntelliJ (édition commerciale) prend en charge la vérification de la validité d'une configuration Spring et signalera les erreurs telles que les violations de type dans la configuration. Sans ce type de prise en charge d'outils, vous ne pouvez pas vérifier si la configuration est valide avant d'exécuter des tests.

C'est une des raisons pour lesquelles le modèle de «gâteau» (comme il est connu de la communauté Scala) est une bonne idée: le câblage entre les composants peut être vérifié par le vérificateur de type. Vous n'avez pas cet avantage avec les annotations ou XML.

  • Cela rend l'analyse statique globale des programmes très difficile.

Des cadres tels que Spring ou Guice rendent difficile la détermination statique de l'apparence du graphique d'objet créé par le conteneur. Bien qu'ils créent un graphique d'objet au démarrage du conteneur, ils ne fournissent pas d'API utiles qui décrivent le graphique d'objet qui / serait / serait créé.

Martin Ellis
la source
1
C'est un taureau absolu. L'injection de dépendances est un concept, elle ne nécessite pas de framework fricken. Tout ce dont vous avez besoin est nouveau. Laissez tomber le printemps et toutes ces conneries, vos outils fonctionneront très bien, et vous pourrez refactoriser votre code beaucoup mieux.
time4tea
C'est vrai, bon point. J'aurais dû être plus clair que je parlais de problèmes avec les frameworks DI et non du modèle. Il est possible que j'ai raté la clarification de la question lorsque j'ai répondu (en supposant qu'elle était là à l'époque).
Martin Ellis
Ah. Dans ce cas, je retire avec plaisir mon agacement. excuses.
time4tea
2

Il semble que les avantages supposés d'un langage à typage statique diminuent considérablement lorsque vous utilisez constamment des techniques pour contourner le typage statique. Une grande boutique Java que je viens d'interviewer a cartographié leurs dépendances de construction avec une analyse de code statique ... qui devait analyser tous les fichiers Spring pour être efficace.

Chris D
la source
1

Cela peut augmenter le temps de démarrage de l'application, car le conteneur IoC doit résoudre les dépendances de manière appropriée et nécessite parfois plusieurs itérations.

romain
la source
3
Juste pour donner un chiffre, un conteneur DI devrait résoudre plusieurs milliers de dépendances par seconde. ( codinginstinct.com/2008/05/… ) Les conteneurs DI permettent une instanciation différée. Les performances ne devraient pas (même dans les applications volumineuses) être un problème. Ou au moins, le problème de performances devrait être résolu et ne devrait pas être une raison de décider contre l'IoC et ses cadres.
Robert