J'ai eu un certain sentiment ces derniers jours que l'injection de dépendance devrait vraiment s'appeler "Je ne peux pas me décider". Je sais que cela peut sembler idiot, mais il s'agit vraiment du raisonnement derrière la raison pour laquelle je devrais utiliser l'injection de dépendance (DI). Souvent, on dit que je devrais utiliser DI, pour atteindre un niveau plus élevé de couplage lâche, et j'obtiens cette partie. Mais vraiment ... à quelle fréquence dois-je changer ma base de données, une fois que mon choix s'est porté sur MS SQL ou MySQL ... Très rarement, n'est-ce pas?
Quelqu'un at-il des raisons très convaincantes pour lesquelles DI est la voie à suivre?
Deux mots, test unitaire.
L'une des raisons les plus convaincantes pour DI est de permettre un test unitaire plus facile sans avoir à frapper une base de données et à se soucier de la configuration des données de "test".
DI est très utile pour découpler votre système. Si tout ce que vous l'utilisez est de découpler l'implémentation de la base de données du reste de votre application, alors soit votre application est assez simple, soit vous devez effectuer beaucoup plus d'analyses sur le domaine problématique et découvrir quels sont les composants de votre domaine problématique les plus susceptibles de changer et les composants de votre système qui ont une grande quantité de couplage.
DI est très utile lorsque vous visez la réutilisation de code, la polyvalence et la robustesse aux changements dans votre domaine problématique.
La pertinence de votre projet dépend de la durée de vie attendue de votre code. Selon le type de travail que vous effectuez sans réutilisation d'un projet à l'autre pour la majorité du code que vous écrivez peut en fait être tout à fait acceptable.
Un exemple d'utilisation de DI consiste à créer une application qui peut être déployée pour plusieurs clients utilisant DI pour injecter des personnalisations pour le client, qui pourrait également être décrite comme le modèle de stratégie GOF. De nombreux modèles GOF peuvent être facilités par l'utilisation d'un cadre DI.
DI est plus pertinent pour le développement d'applications d'entreprise dans lequel vous avez une grande quantité de code, des exigences métier complexes et une attente (ou espoir) que le système sera maintenu pendant de nombreuses années ou décennies.
Même si vous ne changez pas la structure de votre programme pendant les phases de développement, vous découvrirez que vous devez accéder à plusieurs sous-systèmes à partir de différentes parties de votre programme. Avec DI, chacune de vos classes n'a qu'à demander des services et vous n'êtes pas obligé de fournir tout le câblage manuellement.
Cela m'aide vraiment à me concentrer sur l'interaction des choses dans la conception du logiciel et non sur "qui doit transporter quoi parce que quelqu'un d'autre en aura besoin plus tard".
De plus, il enregistre également BEAUCOUP de travail en écrivant du code passe-partout. Ai-je besoin d'un singleton? Je viens de configurer une classe pour en être une. Puis-je tester avec un tel "singleton"? Oui, je peux toujours (puisque je viens de le CONFIGURER pour qu'il n'existe qu'une seule fois, mais le test peut instancier une implémentation alternative).
Mais au fait, avant d'utiliser DI, je ne comprenais pas vraiment sa valeur, mais l'essayer m'a vraiment ouvert les yeux: mes créations sont beaucoup plus orientées objet qu'elles l'ont été auparavant. Soit dit en passant, avec l'application actuelle, JE NE FAIS PAS de test unitaire (mauvais, mauvais moi) mais je ne pouvais TOUJOURS plus vivre avec DI. Il est tellement plus facile de déplacer les choses et de garder les classes petites et simples.
Bien que je sois semi-d'accord avec vous avec l'exemple de base de données, l'une des grandes choses que j'ai trouvées utiles pour utiliser DI est de m'aider à tester la couche que je construis au-dessus de la base de données.
Voici un exemple ...
Vous avez votre base de données.
Vous avez votre code qui accède à la base de données et renvoie des objets
Vous avez des objets de domaine métier qui prennent les objets de l'élément précédent et font une logique avec eux.
Si vous fusionnez l'accès aux données avec votre logique de domaine métier, vos objets de domaine peuvent devenir difficiles à tester. DI vous permet d'injecter vos propres objets d'accès aux données dans votre domaine afin de ne pas dépendre de la base de données pour les tests ou éventuellement les démonstrations (exécution d'une démo où certaines données ont été extraites de xml au lieu d'une base de données).
Résumé des composants tiers et des cadres comme celui-ci pourrait également vous aider.
Mis à part l'exemple de test, il existe quelques endroits où DI peut être utilisé via une approche de conception par contrat. Vous pouvez trouver approprié de créer une sorte de moteur de traitement qui appelle les méthodes des objets que vous y injectez. Bien qu'il ne puisse pas vraiment le "traiter", il exécute les méthodes qui ont une implémentation différente dans chaque objet que vous fournissez.
J'ai vu un exemple de cela où chaque objet de domaine métier avait une fonction "Enregistrer" qui a été appelée après avoir été injectée dans le processeur. Le processeur a modifié le composant avec des informations de configuration et Save a géré l'état principal de l'objet. En substance, DI a complété l'implémentation de la méthode polymorphe des objets conformes à l'interface.
L'injection de dépendances vous permet de tester des unités de code spécifiques de manière isolée.
Disons que j'ai une classe Foo
par exemple qui prend une instance d'une classe Bar
dans son constructeur. L'une des méthodes de Foo
pourrait vérifier qu'une valeur de propriété de Bar
est celle qui permet à un autre traitement de Bar
d'avoir lieu.
public class Foo
{
private Bar _bar;
public Foo(Bar bar)
{
_bar = bar;
}
public bool IsPropertyOfBarValid()
{
return _bar.SomeProperty == PropertyEnum.ValidProperty;
}
}
Supposons maintenant que Bar
soit instancié et que ses propriétés soient définies sur les données d'une source de données dans son constructeur. Comment puis-je procéder pour tester la méthode IsPropertyOfBarValid()
de Foo
(en ignorant le fait qu'il s'agit d'un exemple incroyablement simple)? Eh bien, Foo
dépend de l'instance de Bar
transmise au constructeur, qui à son tour dépend des données de la source de données sur lesquelles ses propriétés sont définies. Ce que nous aimerions faire, c'est avoir un moyen d'isoler Foo
des ressources dont il dépend afin de pouvoir le tester isolément
C'est là que l'Injection de Dépendance entre en jeu. Ce que nous voulons, c'est avoir un moyen de truquer une instance de Bar
passée à Foo
afin que nous puissions contrôler les propriétés définies sur ce faux Bar
et réaliser ce que nous avons prévu de faire, tester que l'implémentation de IsPropertyOfBarValid()
fait ce que nous attendons de lui, c'est-à-dire renvoyer true lorsque Bar.SomeProperty == PropertyEnum.ValidProperty
et false pour toute autre valeur.
Il existe deux types de faux objets, Mocks et Stubs. Les stubs fournissent une entrée pour l'application testée afin que le test puisse être effectué sur autre chose. Les simulations, d'autre part, fournissent une entrée au test pour décider de réussir/échouer.
Martin Fowler a un excellent article sur la différence entre les simulacres et les talons
Mis à part l'accouplement lâche, les tests de tout type sont réalisés avec beaucoup plus de facilité grâce à DI. Vous pouvez mettre à remplacer une dépendance existante d'une classe sous test par une maquette, un mannequin ou même une autre version. Si une classe est créée avec ses dépendances directement instanciées, il peut souvent être difficile, voire impossible, de les "bloquer" si nécessaire.
Je pense que DI vaut la peine d'être utilisé lorsque vous avez de nombreux services/composants dont les implémentations doivent être sélectionnées lors de l'exécution en fonction de la configuration externe. (Notez qu'une telle configuration peut prendre la forme d'un fichier XML ou d'une combinaison d'annotations de code et de classes distinctes; choisissez ce qui est plus pratique.)
Sinon, j'utiliserais simplement un ServiceLocator, qui est beaucoup plus "léger" et plus facile à comprendre qu'un cadre DI complet.
Pour les tests unitaires, je préfère utiliser une API de simulation qui peut simuler des objets à la demande, au lieu d'exiger qu'ils soient "injectés" dans l'unité testée à partir d'un test. Pour Java, une de ces bibliothèques est la mienne, JMockit .
Je viens de comprendre ce soir. Pour moi, l'injection de dépendance est une méthode pour instancier des objets qui nécessitent beaucoup de paramètres pour fonctionner dans un contexte spécifique.
Quand devez-vous utiliser l'injection de dépendance? Vous pouvez utiliser l'injection de dépendance si vous instanciez de manière statique un objet. Par exemple, si vous utilisez une classe qui peut convertir des objets en fichier XML ou JSON et si vous n'avez besoin que du fichier XML. Vous devrez instancier l'objet et configurer beaucoup de choses si vous n'utilisez pas l'injection de dépendance.
Quand ne pas utiliser l'injection de dépendance? Si un objet est instancié avec des paramètres de demande (après un formulaire de soumission), vous ne devez pas utiliser l'injection de dépandance car l'objet n'est pas instancié de manière statique.