Je suis nouveau dans l' injection de dépendances et j'ai quelques questions sur le style à utiliser dans mes applications. Je viens de lire Inversion of Control Containers et le Dependency Injection pattern de Martin Fowler, mais je ne peux pas faire la différence pratique entre le constructeur, le setter et l'injection d'interface.
Il me semble que les raisons d'utiliser l'un sur l'autre ne sont qu'une question de nettoyage et / ou de clarté du code. Quelle est la différence? Y a-t-il des avantages ou des inconvénients importants à utiliser l'un par rapport à l'autre, ou est-ce juste ce que j'ai déclaré auparavant?
À mon avis, l' injection de constructeur est la plus intuitive de toutes, ainsi que l' injection d'interface est la moins. D'un autre côté, l' injection de setter est un moyen terme, mais êtes-vous censé pouvoir changer l'instance de l'objet de dépendance que vous avez initialement injecté? Ce style d'injection garantit-il que l'objet qui a besoin de la dépendance le fera toujours injecter? Je ne crois pas, mais veuillez me corriger si je me trompe.
Réponses:
L'injection de constructeur a l'avantage de rendre la dépendance explicite et d'obliger le client à fournir une instance. Il peut également garantir que le client ne pourra pas modifier l'instance ultérieurement. Un inconvénient (possible) est que vous devez ajouter un paramètre à votre constructeur.
Setter Injection a l'avantage de ne pas nécessiter l'ajout d'un paramètre au constructeur. Il ne nécessite pas non plus que le client définisse l'instance. Ceci est utile pour les dépendances facultatives. Cela peut également être utile si vous souhaitez que la classe crée, par exemple, un référentiel de données réel par défaut, puis dans un test, vous pouvez utiliser le setter pour le remplacer par une instance de test.
L'injection d'interface , pour autant que je sache, n'est pas très différente de l'injection de setter. Dans les deux cas, vous définissez (en option) une dépendance qui peut être modifiée ultérieurement.
En fin de compte, c'est une question de préférence et de savoir si une dépendance est requise ou non . Personnellement, j'utilise l'injection constructeur presque exclusivement. J'aime que cela rend les dépendances d'une classe explicites en forçant le client à fournir une instance dans le constructeur. J'aime aussi que le client ne puisse pas changer l'instance après coup.
Souvent, ma seule raison de passer dans deux implémentations distinctes est pour les tests. En production, je peux réussir un
DataRepository
, mais en test, je passerais unFakeDataRepository
. Dans ce cas, je fournirai généralement deux constructeurs: un sans paramètres et un autre qui accepte aIDataRepository
. Ensuite, dans le constructeur sans paramètres, je vais enchaîner un appel au deuxième constructeur et passer unnew DataRepository()
.Voici un exemple en C #:
C'est ce qu'on appelle l'injection de dépendance du pauvre. Je l'aime parce que dans le code client de production, je n'ai pas besoin de me répéter en ayant plusieurs instructions répétées qui ressemblent à
Cependant, je peux toujours passer dans une autre implémentation pour les tests. Je me rends compte qu'avec Poor Man's DI, je coder en dur ma dépendance, mais c'est acceptable pour moi car j'utilise principalement DI pour les tests.la source
Les différences entre l'injection de constructeur et de setter sont déjà décrites de manière adéquate ci-dessus, donc je ne les développerai pas davantage.
L'injection d'interface est une forme d'injection plus avancée qui est utile car elle permet de décider de la dépendance au moment de son utilisation plutôt que lors de l'initialisation de l'objet qui l'utilisera. Cela permet un certain nombre d'options utiles:
La dépendance peut être définie différemment de l'objet dans lequel elle est injectée; par exemple, vous pouvez utiliser l'injection d'interface pour fournir un objet pour lequel il existe par session utilisateur ou par thread à un singleton global. Chaque fois que l'objet a besoin de la dépendance, il appellera la méthode getter fournie par le framework, ce qui peut renvoyer des résultats différents selon la situation dans laquelle il est appelé.
Il permet une initialisation paresseuse - il n'est pas nécessaire d'initialiser la dépendance jusqu'à ce qu'elle soit sur le point d'être utilisée
Il permet aux dépendances d'être chargées à partir d'une copie mise en cache lorsqu'elles existent ou réinitialisées lorsqu'elles n'existent pas (par exemple en utilisant un
SoftReference
en Java).De toute évidence, des techniques avancées comme celle-ci ont des inconvénients; dans ce cas, le problème principal est que le code devient moins clair (les classes utilisées dans votre code deviennent abstraites, et il n'y a pas d'implémentation concrète évidente, ce qui peut être déroutant si vous n'y êtes pas habitué) et vous devenez plus dépendant sur votre framework d'injection de dépendances (il est toujours possible d'instancier vos objets manuellement, bien sûr, mais c'est plus difficile qu'avec d'autres styles d'injection).
la source