Au niveau de la classe, c'est facile.
"Dependency Injection" répond simplement à la question "comment vais-je trouver mes collaborateurs" avec "ils sont poussés à vous - vous n'avez pas à aller les chercher vous-même". (Cela ressemble à - mais pas la même chose que - 'Inversion of Control' où la question "comment vais-je ordonner mes opérations sur mes entrées?" A une réponse similaire).
Le seul avantage de vos collaborateurs est que le code client peut utiliser votre classe pour composer un graphe d'objet adapté à ses besoins actuels ... vous n'avez pas prédéterminé de manière arbitraire la forme et la mutabilité du graphe en décidant en privé les types concrets et le cycle de vie de vos collaborateurs.
(Tous ces autres avantages, de testabilité, de couplage lâche, etc., découlent en grande partie de l'utilisation d'interfaces et pas tellement de la dépendance par injection, bien que la DI favorise naturellement l'utilisation d'interfaces).
Il est à noter que, si vous évitez d'instancier vos propres collaborateurs, votre classe doit donc obtenir ses collaborateurs d'un constructeur, d'une propriété ou d'un argument de méthode (cette dernière option est souvent négligée, d'ailleurs ... Il n’est pas toujours logique que les collaborateurs d’une classe fassent partie de son «état»).
Et c'est une bonne chose.
Au niveau de l'application ...
Voilà pour la vue par classe des choses. Supposons que plusieurs classes suivent la règle "ne pas instancier vos propres collaborateurs" et que vous souhaitiez en faire une application. La solution la plus simple consiste à utiliser le bon vieux code (un outil très utile pour appeler des constructeurs, des propriétés et des méthodes!) Afin de composer le graphe d'objet souhaité et de lancer des entrées. (Oui, certains objets de votre graphe seront eux-mêmes des fabriques d’objets, qui ont été transmises en tant que collaborateurs à d’autres objets de longue durée dans le graphe, prêts au service. Vous ne pouvez pas pré-construire tous les objets! ).
... votre besoin de configurer de manière "flexible" le graphe d'objet de votre application ...
En fonction de vos autres objectifs (non liés au code), vous pouvez donner à l'utilisateur final un contrôle sur le graphe d'objets ainsi déployé. Cela vous conduit vers un schéma de configuration, d’une sorte ou d’une autre, qu’il s’agisse d’un fichier texte de votre propre conception avec des paires nom / valeur, un fichier XML, un DSL personnalisé, un langage de description de graphe déclaratif tel que YAML, un langage de script impératif tel que JavaScript ou autre chose appropriée à la tâche à accomplir. Peu importe ce qu'il faut pour composer un graphe d'objet valide, de manière à répondre aux besoins de vos utilisateurs.
... peut être une force de conception significative.
Dans les circonstances les plus extrêmes de ce type, vous pouvez choisir une approche très générale et donner aux utilisateurs finaux un mécanisme général permettant de «câbler» le graphe d'objet de leur choix et même de leur permettre de fournir des réalisations concrètes d'interfaces au runtime! (Votre documentation est un joyau étincelant, vos utilisateurs sont très intelligents, ils connaissent au moins les grandes lignes du graphe d'objets de votre application, mais ils ne disposent pas d'un compilateur à portée de main). Ce scénario peut théoriquement se produire dans certaines situations «d'entreprise».
Dans ce cas, vous disposez probablement d'un langage déclaratif qui permet à vos utilisateurs d'exprimer n'importe quel type, de la composition d'un graphe d'objets de ce type et d'une palette d'interfaces que l'utilisateur final mythique peut mélanger et assortir. Pour réduire la charge cognitive de vos utilisateurs, vous préférez une approche de «configuration par convention», de sorte qu'ils n'aient plus qu'à intervenir et à passer outre le fragment objet-graphe d'intérêt, au lieu de se débattre dans son ensemble.
Vous pauvre pauvre merde!
Parce que vous n'avez pas envie d'écrire tout cela vous-même (mais sérieusement, vérifiez une liaison YAML pour votre langue), vous utilisez un cadre DI quelconque.
En fonction de la maturité de ce cadre, vous n’avez peut-être pas la possibilité d’utiliser une injection par constructeur, même lorsque cela a du sens (les collaborateurs ne changent pas pendant la durée de vie d’un objet), vous obligeant ainsi à utiliser Setter Injection (même lorsque les collaborateurs ne changent pas au cours de la vie d'un objet, et même lorsqu'il n'y a pas vraiment de raison logique pour laquelle toute implémentation concrète d'une interface doit avoir des collaborateurs d'un type spécifique). Si c'est le cas, vous êtes actuellement dans l'enfer à fort couplage, malgré les "interfaces utilisées" avec diligence dans votre code-base - horreur!
Espérons cependant que vous avez utilisé un framework DI qui vous offre une option d’injection de constructeur, et que vos utilisateurs ne sont que grincheux de ne pas perdre plus de temps à réfléchir aux choses spécifiques qu’ils ont besoin de configurer et à leur donner une interface utilisateur plus adaptée à la tâche. à portée de main. (Bien que pour être juste, vous avez probablement essayé de penser à un moyen, mais JavaEE vous a laissé tomber et vous avez dû recourir à ce hack horrible).
Note de démarrage
Vous n’utilisez jamais Google Guice, ce qui permet au codeur de se passer de la tâche de composer un graphe-objet avec du code ... en écrivant du code. Argh!