Qu'est-ce que l'injection de dépendance?


3075

Plusieurs questions ont déjà été publiées avec des questions spécifiques sur l' injection de dépendance , telles que le moment de l'utiliser et les cadres disponibles. cependant,

Qu'est-ce que l'injection de dépendance et quand / pourquoi doit-elle ou ne doit-elle pas être utilisée?


Voir ma discussion sur l'injection de dépendance ici .
Kevin S.

33
Je suis d'accord avec les commentaires concernant les liens. Je peux comprendre que vous souhaitiez faire référence à quelqu'un d'autre. Mais ajoutez au moins pourquoi vous les liez et ce qui rend ce lien meilleur que les autres liens que je pourrais obtenir en utilisant Google
Christian Payne

@AR: Techniquement, l'injection de dépendance n'est pas une forme spéciale d'IoC. Au contraire, l'IoC est une technique utilisée pour fournir l'injection de dépendance. D'autres techniques pourraient être utilisées pour fournir une injection de dépendance (bien que l'IoC soit la seule couramment utilisée), et l'IoC est également utilisé pour de nombreux autres problèmes.
Sean Reilly

L'une des plus belles explications que j'ai jamais lues à propos de DI provient de Google's Guice (prononcé comme jus) http://code.google.com/p/google-guice/wiki/Motivation?tm=6
Raj

136
Concernant les liens, n'oubliez pas qu'ils disparaissent souvent d'une manière ou d'une autre. Il y a un nombre croissant de liens morts dans les réponses SO. Donc, peu importe la qualité de l'article lié, ce n'est pas bon du tout si vous ne le trouvez pas.
DOK

Réponses:


1931

L'injection de dépendance transmet la dépendance à d'autres objets ou framework (injecteur de dépendance).

L'injection de dépendances facilite les tests. L'injection peut être effectuée par le constructeur .

SomeClass() a son constructeur comme suit:

public SomeClass() {
    myObject = Factory.getObject();
}

Problème : Si myObjectimplique des tâches complexes telles que l'accès au disque ou l'accès au réseau, il est difficile de faire un test unitaire sur SomeClass(). Les programmeurs doivent se moquer myObjectet peuvent intercepter l'appel d'usine.

Solution alternative :

  • Passer myObjecten argument au constructeur
public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

myObject peut être passé directement, ce qui facilite les tests.

  • Une alternative courante consiste à définir un constructeur à ne rien faire . L'injection de dépendances peut être effectuée par l'intermédiaire des setters. (h / t @MikeVella).
  • Martin Fowler documente une troisième alternative (h / t @MarcDix), où les classes implémentent explicitement une interface pour les dépendances que les programmeurs souhaitent injecter.

Il est plus difficile d'isoler les composants dans les tests unitaires sans injection de dépendance.

En 2013, lorsque j'ai écrit cette réponse, c'était un thème majeur sur le blog de test Google . Cela reste le plus grand avantage pour moi, car les programmeurs n'ont pas toujours besoin de la flexibilité supplémentaire dans leur conception d'exécution (par exemple, pour le localisateur de services ou des modèles similaires). Les programmeurs doivent souvent isoler les classes pendant les tests.


25
Reconnaissant que la référence de Ben Hoffstein à l'article de Martin Fowler est nécessaire comme pointant une lecture incontournable sur le sujet, j'accepte la réponse de wds car elle répond en fait à la question ici sur SO.
AR.

121
+1 pour explication et motivation: faire de la création d'objets dont une classe dépend le problème de quelqu'un d'autre . Une autre façon de dire que DI rend les classes plus cohérentes (elles ont moins de responsabilités).
Fuhrmanator

13
Vous dites que la dépendance est transmise "au constructeur", mais si je comprends bien, ce n'est pas strictement vrai. C'est toujours une injection de dépendance si la dépendance est définie comme une propriété après que l'objet a été instancié, n'est-ce pas?
Mike Vella

1
@MikeVella Oui, c'est exact. Cela ne fait aucune différence dans la plupart des cas, bien que les propriétés soient généralement un peu plus flexibles. Je vais modifier légèrement le texte pour le souligner.
wds

2
L'une des meilleures réponses que j'ai trouvées jusqu'à présent, donc je suis vraiment intéressé à l'améliorer. Il manque une description de la troisième forme d'injection de dépendance: l' injection d'interface .
Marc Dix

2351

La meilleure définition que j'ai trouvée jusqu'à présent est celle de James Shore :

"Injection de dépendance" est un terme de 25 dollars pour un concept de 5 cents. [...] L'injection de dépendance signifie donner à un objet ses variables d'instance. [...].

Il y a un article de Martin Fowler qui peut aussi s'avérer utile.

L'injection de dépendances fournit essentiellement les objets dont un objet a besoin (ses dépendances) au lieu de le faire construire lui-même. C'est une technique très utile pour les tests, car elle permet de moquer ou de bloquer les dépendances.

Les dépendances peuvent être injectées dans les objets par de nombreux moyens (comme l'injection de constructeur ou l'injection de setter). On peut même utiliser des cadres d'injection de dépendance spécialisés (par exemple Spring) pour ce faire, mais ils ne sont certainement pas nécessaires. Vous n'avez pas besoin de ces cadres pour avoir une injection de dépendance. L'instanciation et le passage d'objets (dépendances) de manière explicite est tout aussi bien une injection qu'une injection par framework.


35
J'aime l'explication de l'article de James, en particulier la fin: "Pourtant, vous devez vous émerveiller devant toute approche qui prend trois concepts (" TripPlanner "," CabAgency "et" AirlineAgency "), les transforme en neuf classes, puis ajoute des dizaines de lignes de code de collage et de configuration XML avant l'écriture d'une seule ligne de logique d'application. " C'est ce que j'ai vu très souvent (malheureusement) - que l'injection de dépendance (ce qui est bon en soi, comme il l'explique) est utilisée à mauvais escient pour compliquer des choses qui auraient pu être plus faciles - finir par écrire du code "de support" ...
Matt

2
Re: "Instancier et passer des objets (dépendances) explicitement est tout aussi bien une injection qu'une injection par framework.". Alors pourquoi les gens ont fait des cadres pour ça?
dzieciou

13
Pour la même raison que chaque framework est (ou du moins devrait être) écrit: car il y a beaucoup de code répété / passe-partout qui doit être écrit une fois que vous atteignez une certaine complexité. Le problème est souvent que les gens recherchent un cadre même s'il n'est pas strictement nécessaire.
Thiago Arrais

14
Terme de 25 $ pour un concept de 5 cents est mort. Voici un bon article qui m'a aidé: codeproject.com/Articles/615139/…
Christine

@Matt les fichiers de configuration ne sont que le «report de la décision» porté à son extrême - «report de la décision jusqu'à l'exécution réelle». Dagger et surtout Dagger ont, à mon avis, trouvé le bon endroit "reporter la décision jusqu'au moment du montage de l'application".
Thorbjørn Ravn Andersen

645

J'ai trouvé cet exemple amusant en termes de couplage lâche :

Toute application est composée de nombreux objets qui collaborent entre eux pour effectuer des tâches utiles. Traditionnellement, chaque objet est responsable d'obtenir ses propres références aux objets dépendants (dépendances) avec lesquels il collabore. Cela conduit à des classes hautement couplées et à un code difficile à tester.

Par exemple, considérons un Carobjet.

A Cardépend des roues, du moteur, du carburant, de la batterie, etc. pour fonctionner. Traditionnellement, nous définissons la marque de ces objets dépendants ainsi que la définition de l' Carobjet.

Sans injection de dépendance (DI):

class Car{
  private Wheel wh = new NepaliRubberWheel();
  private Battery bt = new ExcideBattery();

  //The rest
}

Ici, l' Carobjet est responsable de la création des objets dépendants.

Et si nous voulons changer le type de son objet dépendant - disons Wheel- après les NepaliRubberWheel()piqûres initiales ? Nous devons recréer l'objet Car avec sa nouvelle dépendance ChineseRubberWheel(), mais seul le Carfabricant peut le faire.

Alors qu'est-ce que cela Dependency Injectionfait pour nous ...?

Lorsque vous utilisez l'injection de dépendance, les objets reçoivent leurs dépendances au moment de l'exécution plutôt qu'au moment de la compilation (temps de fabrication de la voiture) . Afin que nous puissions maintenant changer le Wheelmoment où nous voulons. Ici, le dependency( wheel) peut être injecté Carau moment de l'exécution.

Après avoir utilisé l'injection de dépendance:

Ici, nous injectons les dépendances (roue et batterie) lors de l'exécution. D'où le terme: injection de dépendance.

class Car{
  private Wheel wh; // Inject an Instance of Wheel (dependency of car) at runtime
  private Battery bt; // Inject an Instance of Battery (dependency of car) at runtime
  Car(Wheel wh,Battery bt) {
      this.wh = wh;
      this.bt = bt;
  }
  //Or we can have setters
  void setWheel(Wheel wh) {
      this.wh = wh;
  }
}

Source: Comprendre l'injection de dépendance


20
La façon dont je comprends cela est qu'au lieu d'instancier un nouvel objet en tant que partie d'un autre objet, nous pouvons injecter ledit objet quand et si cela est nécessaire, supprimant ainsi la dépendance du premier objet à son égard. Est-ce correct?
JeliBeanMachine

J'ai décrit cela avec un exemple de café ici: digigene.com/design-patterns/dependency-injection-coffeeshop
Ali Nem

11
J'aime vraiment cette analogie car c'est un anglais simple utilisant une analogie simple. Disons que je suis Toyota, déjà dépensé trop financièrement et le pouvoir de l' homme sur la fabrication d' une voiture de la conception à rouler hors ASSEMBLE ligne, s'il y a déjà des producteurs de pneus de bonne réputation, pourquoi devrais - je commencer à partir de zéro pour faire une division de fabrication des pneus c. - à - newun pneu? Je ne. Tout ce que j'ai à faire, c'est de les acheter (injecter via param), d'installer et wah-lah! Donc, pour en revenir à la programmation, disons qu'un projet C # doit utiliser une bibliothèque / classe existante, il existe deux façons d'exécuter / déboguer, 1-ajouter une référence à l'ensemble du projet de cela
Jeb50

(suite), .. bibliothèque / classe externe, ou 2-ajoutez-le à partir de la DLL. Sauf si nous devons voir ce qu'il y a à l'intérieur de cette classe externe, l'ajouter en tant que DLL est un moyen plus simple. Donc l'option 1 est pour newelle, l'option 2 est de la transmettre en tant que param. Peut ne pas être précis, mais simple stupide facile à comprendre.
Jeb50

1
@JeliBeanMachine (désolé pour la réponse extrêmement tardive à un commentaire ..) ce n'est pas que nous supprimons la dépendance du premier objet sur l'objet roue ou l'objet batterie, c'est que nous lui transmettons la dépendance, afin que nous puissions changer l'instance ou l'implémentation de la dépendance. Avant: la voiture a une dépendance codée en dur sur NepaliRubberWheel. Après: la voiture a une dépendance injectée sur l'instance de Wheel.
Mikael Ohlson

263

L'injection de dépendance est une pratique dans laquelle les objets sont conçus de manière à recevoir des instances des objets provenant d'autres morceaux de code, au lieu de les construire en interne. Cela signifie que tout objet implémentant l'interface requise par l'objet peut être remplacé sans modifier le code, ce qui simplifie les tests et améliore le découplage.

Par exemple, considérez ces classes:

public class PersonService {
  public void addManager( Person employee, Person newManager ) { ... }
  public void removeManager( Person employee, Person oldManager ) { ... }
  public Group getGroupByManager( Person manager ) { ... }
}

public class GroupMembershipService() {
  public void addPersonToGroup( Person person, Group group ) { ... }
  public void removePersonFromGroup( Person person, Group group ) { ... }
} 

Dans cet exemple, la mise en œuvre de PersonService::addManageret PersonService::removeManageraurait besoin d'une instance de GroupMembershipServiceafin de faire son travail. Sans injection de dépendance, la manière traditionnelle de procéder serait d'instancier une nouvelle GroupMembershipServicedans le constructeur de PersonServiceet d'utiliser cet attribut d'instance dans les deux fonctions. Cependant, si le constructeur de GroupMembershipServicea plusieurs choses dont il a besoin, ou pire encore, il y a des "setters" d'initialisation qui doivent être appelés sur le GroupMembershipService, le code se développe assez rapidement, et le PersonServicemaintenant dépend non seulement du GroupMembershipServicemais aussi de tout ce qui GroupMembershipServicedépend de. En outre, le lien avec GroupMembershipServiceest codé en dur dans le PersonServicequi signifie que vous ne pouvez pas "simuler" unGroupMembershipService à des fins de test ou pour utiliser un modèle de stratégie dans différentes parties de votre application.

Avec l'injection de dépendances, au lieu d'instancier le GroupMembershipServicedans votre PersonService, vous devez le transmettre au PersonServiceconstructeur, ou bien ajouter une propriété (getter et setter) pour en définir une instance locale. Cela signifie que vous n'avez PersonServiceplus à vous soucier de la façon de créer un GroupMembershipService, il accepte simplement ceux qui lui sont donnés et fonctionne avec eux. Cela signifie également que tout ce qui est une sous-classe de GroupMembershipService, ou implémente l' GroupMembershipServiceinterface, peut être "injecté" dans le PersonService, et le PersonServicen'a pas besoin de connaître le changement.


29
Cela aurait été génial si vous pouviez donner le même exemple de code APRÈS avoir utilisé DI
CodyBugstein

170

La réponse acceptée est bonne - mais je voudrais ajouter à cela que DI ressemble beaucoup à l'évitement classique des constantes codées en dur dans le code.

Lorsque vous utilisez une constante comme un nom de base de données, vous la déplacez rapidement de l'intérieur du code vers un fichier de configuration et passez une variable contenant cette valeur à l'endroit où elle est nécessaire. La raison en est que ces constantes changent généralement plus fréquemment que le reste du code. Par exemple, si vous souhaitez tester le code dans une base de données de test.

DI est analogue à cela dans le monde de la programmation orientée objet. Les valeurs à la place des littéraux constants sont des objets entiers - mais la raison pour déplacer le code en les créant hors du code de classe est similaire - les objets changent plus fréquemment que le code qui les utilise. Les tests sont un cas important où un tel changement est nécessaire.


18
+1 "les objets changent plus fréquemment que le code qui les utilise". Pour généraliser, ajoutez une indirection aux points de flux. Selon le point de flux, les indirections sont appelées par des noms différents !!
Chethan

139

Essayons un exemple simple avec les classes Car et Engine , toute voiture a besoin d'un moteur pour aller n'importe où, du moins pour l'instant. Voici donc à quoi ressemblera le code sans injection de dépendance.

public class Car
{
    public Car()
    {
        GasEngine engine = new GasEngine();
        engine.Start();
    }
}

public class GasEngine
{
    public void Start()
    {
        Console.WriteLine("I use gas as my fuel!");
    }
}

Et pour instancier la classe Car, nous utiliserons le code suivant:

Car car = new Car();

Le problème avec ce code que nous avons étroitement couplé à GasEngine et si nous décidons de le changer en ElectricityEngine, nous devrons réécrire la classe Car. Et plus l'application est grande, plus nous devrons ajouter de problèmes et de maux de tête et utiliser un nouveau type de moteur.

En d'autres termes, avec cette approche, notre classe Car de haut niveau dépend de la classe GasEngine de niveau inférieur qui viole le principe d'inversion de dépendance (DIP) de SOLID. DIP suggère que nous devrions dépendre d'abstractions et non de classes concrètes. Donc, pour satisfaire cela, nous introduisons l'interface IEngine et réécrivons le code comme ci-dessous:

    public interface IEngine
    {
        void Start();
    }

    public class GasEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I use gas as my fuel!");
        }
    }

    public class ElectricityEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I am electrocar");
        }
    }

    public class Car
    {
        private readonly IEngine _engine;
        public Car(IEngine engine)
        {
            _engine = engine;
        }

        public void Run()
        {
            _engine.Start();
        }
    }

Maintenant, notre classe Car dépend uniquement de l'interface IEngine, pas d'une implémentation spécifique du moteur. Maintenant, la seule astuce est de savoir comment créer une instance de la voiture et lui donner une classe de moteur concrète réelle comme GasEngine ou ElectricityEngine. C'est là qu'intervient l'injection de dépendance .

   Car gasCar = new Car(new GasEngine());
   gasCar.Run();
   Car electroCar = new Car(new ElectricityEngine());
   electroCar.Run();

Ici, nous injectons (passons) essentiellement notre dépendance (instance Engine) au constructeur Car. Alors maintenant, nos classes ont un couplage lâche entre les objets et leurs dépendances, et nous pouvons facilement ajouter de nouveaux types de moteurs sans changer la classe Car.

Le principal avantage de l' injection de dépendance est que les classes sont couplées de manière plus lâche, car elles n'ont pas de dépendances codées en dur. Cela suit le principe d'inversion de dépendance, qui a été mentionné ci-dessus. Au lieu de référencer des implémentations spécifiques, les classes demandent des abstractions (généralement des interfaces ) qui leur sont fournies lors de la construction de la classe.

Donc, à la fin de l' injection de dépendance n'est qu'une technique pour réaliser un couplage lâche entre les objets et leurs dépendances. Plutôt que d'instancier directement les dépendances dont la classe a besoin pour effectuer ses actions, les dépendances sont fournies à la classe (le plus souvent) via l'injection de constructeur.

De plus, lorsque nous avons de nombreuses dépendances, il est très judicieux d'utiliser des conteneurs d'inversion de contrôle (IoC) qui nous permettent de savoir quelles interfaces doivent être mappées à quelles implémentations concrètes pour toutes nos dépendances et nous pouvons le faire résoudre ces dépendances pour nous lors de la construction. notre objet. Par exemple, nous pourrions spécifier dans le mappage du conteneur IoC que la dépendance IEngine doit être mappée à la classe GasEngine et lorsque nous demandons au conteneur IoC une instance de notre classe Car , il construira automatiquement notre classe Car avec une dépendance GasEngine passé.

MISE À JOUR: J'ai regardé le cours sur EF Core de Julie Lerman récemment et j'ai également aimé sa courte définition sur DI.

L'injection de dépendances est un modèle permettant à votre application d'injecter des objets à la volée aux classes qui en ont besoin, sans forcer ces classes à être responsables de ces objets. Il permet à votre code d'être couplé de manière plus lâche, et Entity Framework Core se connecte à ce même système de services.


2
juste par curiosité, en quoi est-ce différent du modèle de stratégie? Ce modèle encapsule les algorithmes et les rend interchangeables. Il semble que l'injection de dépendance et les modèles de stratégie soient très similaires.
elixir

110

Imaginons que vous souhaitiez aller à la pêche:

  • Sans injection de dépendance, vous devez vous occuper de tout vous-même. Vous devez trouver un bateau, acheter une canne à pêche, chercher des appâts, etc. C'est possible, bien sûr, mais cela vous impose beaucoup de responsabilités. En termes de logiciel, cela signifie que vous devez effectuer une recherche pour toutes ces choses.

  • Avec l'injection de dépendance, quelqu'un d'autre s'occupe de toute la préparation et met à votre disposition le matériel nécessaire. Vous recevrez ("être injecté") le bateau, la canne à pêche et l'appât - tous prêts à l'emploi.


59
Le revers de la médaille est, imaginez que vous embauchez un plombier pour refaire votre salle de bain, qui dit alors: "Super, voici une liste des outils et du matériel que j'ai besoin de vous pour moi". Cela ne devrait-il pas être le travail du plombier?
jscs

Pour que quelqu'un ait besoin de s'occuper d'une personne, il n'a rien à faire. Mais décide quand même de rassembler la liste des bateaux, bâtons et appâts - bien que prêts à l'emploi.
Chookoos du

22
@JoshCaswell Non, ce serait le boulot du plombier. En tant que client, vous devez effectuer la plomberie. Pour cela, vous avez besoin d'un plombier. Le plombier a besoin de ses outils. Pour les obtenir, il est équipé par l'entreprise de plomberie. En tant que client, vous ne voulez pas savoir exactement ce que le plombier fait ou a besoin. En tant que plombier, vous savez ce dont vous avez besoin, mais vous voulez juste faire votre travail, ne pas tout obtenir. En tant qu'employeur de plombier, vous êtes responsable de pourvoir vos plombiers de ce dont ils ont besoin avant de les envoyer chez des gens.
sara

@kai, je comprends votre point. Dans le logiciel, nous parlons d'une usine, n'est-ce pas? Mais DI signifie également que la classe n'utilise pas d'usine car elle n'est toujours pas injectée. Vous, le client, devrez contacter l'employeur (usine) pour vous donner les outils, afin que vous puissiez passer au plombier. N'est-ce pas ainsi que cela fonctionnerait dans un programme? Ainsi, même si le client (appelant classe / fonction / quoi que ce soit) n'a pas à se procurer les outils, il doit tout de même être l'intermédiaire pour s'assurer qu'il parvient au plombier (classe injectée) de l'employeur (usine).
KingOfAllTrades

1
@KingOfAllTrades: Bien sûr, à un moment donné, vous devez avoir quelqu'un qui emploie et équiper des plombiers, ou vous n'avez pas de plombiers. Mais le client ne le fait pas. Le client demande simplement un plombier et en obtient un déjà équipé de ce dont il a besoin pour faire son travail. Avec DI, vous avez encore éventuellement du code pour remplir les dépendances. Mais vous le séparez du code qui fonctionne vraiment. Si vous le prenez à son maximum, vos objets font simplement connaître leurs dépendances et la construction du graphe d'objet se produit à l'extérieur, souvent dans le code init.
cHao

102

Voici l'explication la plus simple sur l' injection de dépendance et le conteneur d'injection de dépendance que j'ai jamais vue:

Sans injection de dépendance

  • L'application a besoin de Foo (par exemple un contrôleur), donc:
  • L'application crée Foo
  • L'application appelle Foo
    • Foo a besoin de Bar (par exemple un service), donc:
    • Foo crée Bar
    • Foo appelle Bar
      • Bar a besoin de Bim (un service, un référentiel,…), donc:
      • Bar crée Bim
      • Le bar fait quelque chose

Avec injection de dépendance

  • L'application a besoin de Foo, qui a besoin de Bar, qui a besoin de Bim, donc:
  • L'application crée Bim
  • L'application crée Bar et lui donne Bim
  • L'application crée Foo et lui donne Bar
  • L'application appelle Foo
    • Foo appelle Bar
      • Le bar fait quelque chose

Utilisation d'un conteneur d'injection de dépendances

  • L'application a besoin de Foo donc:
  • L'application obtient Foo du conteneur, donc:
    • Le conteneur crée Bim
    • Container crée Bar et lui donne Bim
    • Le conteneur crée Foo et lui donne Bar
  • L'application appelle Foo
    • Foo appelle Bar
      • Le bar fait quelque chose

L'injection de dépendance et les conteneurs d'injection de dépendance sont des choses différentes:

  • L'injection de dépendance est une méthode pour écrire un meilleur code
  • un conteneur DI est un outil pour aider à injecter des dépendances

Vous n'avez pas besoin d'un conteneur pour faire l'injection de dépendance. Cependant, un conteneur peut vous aider.



55

L '«injection de dépendance» ne signifie-t-elle pas uniquement l'utilisation de constructeurs paramétrés et de setters publics?

L'article de James Shore montre les exemples suivants à titre de comparaison .

Constructeur sans injection de dépendance:

public class Example { 
  private DatabaseThingie myDatabase; 

  public Example() { 
    myDatabase = new DatabaseThingie(); 
  } 

  public void doStuff() { 
    ... 
    myDatabase.getData(); 
    ... 
  } 
} 

Constructeur avec injection de dépendances:

public class Example { 
  private DatabaseThingie myDatabase; 

  public Example(DatabaseThingie useThisDatabaseInstead) { 
    myDatabase = useThisDatabaseInstead; 
  }

  public void doStuff() { 
    ... 
    myDatabase.getData(); 
    ... 
  } 
}

Sûrement dans la version DI, vous ne voudriez pas initialiser l'objet myDatabase dans le constructeur sans argument? Cela ne semble pas utile et servirait à lever une exception si vous essayez d'appeler DoStuff sans appeler le constructeur surchargé?
Matt Wilko

Uniquement si new DatabaseThingie()ne génère pas d'instance myDatabase valide.
JaneGoodall

40

Pour rendre le concept d'injection de dépendances simple à comprendre. Prenons un exemple de bouton de commutation pour basculer (allumer / éteindre) une ampoule.

Sans injection de dépendance

Le commutateur doit savoir à l'avance à quelle ampoule je suis connecté (dépendance codée en dur). Donc,

Interrupteur -> PermanentBulb // l'interrupteur est directement connecté à l'ampoule permanente, le test n'est pas possible facilement

Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}

Avec injection de dépendance

Switch sait seulement que je dois allumer / éteindre quelle que soit l'ampoule qui m'est transmise. Donc,

Switch -> Bulb1 OU Bulb2 OU NightBulb (dépendance injectée)

Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}

Modification de l' exemple de James pour le commutateur et l'ampoule:

public class SwitchTest { 
  TestToggleBulb() { 
    MockBulb mockbulb = new MockBulb(); 

    // MockBulb is a subclass of Bulb, so we can 
    // "inject" it here: 
    Switch switch = new Switch(mockBulb); 

    switch.ToggleBulb(); 
    mockBulb.AssertToggleWasCalled(); 
  } 
}

public class Switch { 
  private Bulb myBulb; 

  public Switch() { 
    myBulb = new Bulb(); 
  } 

  public Switch(Bulb useThisBulbInstead) { 
    myBulb = useThisBulbInstead; 
  } 

  public void ToggleBulb() { 
    ... 
    myBulb.Toggle(); 
    ... 
  } 
}`

36

Qu'est-ce que l'injection de dépendance (DI)?

Comme d'autres l'ont dit, l' injection de dépendance (DI) supprime la responsabilité de la création directe et de la gestion de la durée de vie d'autres instances d'objet dont dépend notre classe d'intérêt (classe de consommateurs) (au sens UML ). Ces instances sont plutôt transmises à notre classe de consommateur, généralement en tant que paramètres de constructeur ou via des paramètres de propriété (la gestion de l'instanciation de l'objet de dépendance et son passage à la classe de consommateur est généralement effectuée par un conteneur IoC (Inversion of Control) , mais c'est un autre sujet) .

DI, DIP et SOLID

Plus précisément, dans le paradigme des principes SOLID de conception orientée objet de Robert C Martin , DIest l'une des implémentations possibles du principe d'inversion de dépendance (DIP) . Le DIP est Ddu SOLIDmantra - autres implémentations DIP comprennent le service de localisation, et les modèles Plugin.

L'objectif du DIP est de découpler les dépendances étroites et concrètes entre les classes, et au lieu de cela, de desserrer le couplage au moyen d'une abstraction, qui peut être réalisée via un interface, abstract classou pure virtual class, selon le langage et l'approche utilisés.

Sans le DIP, notre code (j'ai appelé cette «classe consommatrice») est directement couplé à une dépendance concrète et est également souvent chargé de savoir comment obtenir et gérer une instance de cette dépendance, c'est-à-dire conceptuellement:

"I need to create/use a Foo and invoke method `GetBar()`"

Alors qu'après l'application du DIP, l'exigence est assouplie et le souci d'obtenir et de gérer la durée de vie de la Foodépendance a été supprimé:

"I need to invoke something which offers `GetBar()`"

Pourquoi utiliser DIP (et DI)?

Le découplage des dépendances entre classes de cette manière permet de remplacer facilement ces classes de dépendance par d'autres implémentations qui remplissent également les conditions préalables de l'abstraction (par exemple, la dépendance peut être commutée avec une autre implémentation de la même interface). De plus, comme d'autres l'ont mentionné, la raison la plus courante pour découpler des classes via le DIP est de permettre à une classe consommatrice d'être testée isolément, car ces mêmes dépendances peuvent désormais être tronquées et / ou simulées.

Une des conséquences de DI est que la gestion de la durée de vie des instances d'objets de dépendance n'est plus contrôlée par une classe consommatrice, car l'objet de dépendance est maintenant passé dans la classe consommatrice (via l'injection de constructeur ou de setter).

Cela peut être vu de différentes manières:

  • Si le contrôle de la durée de vie des dépendances par la classe consommatrice doit être conservé, le contrôle peut être rétabli en injectant une fabrique (abstraite) pour créer les instances de classe de dépendance, dans la classe consommateur. Le consommateur pourra obtenir des instances via un Createsur l'usine selon les besoins, et disposer de ces instances une fois terminées.
  • Ou, le contrôle de la durée de vie des instances de dépendance peut être abandonné à un conteneur IoC (plus d'informations à ce sujet ci-dessous).

Quand utiliser DI?

  • Lorsqu'il sera probablement nécessaire de substituer une dépendance à une implémentation équivalente,
  • Chaque fois que vous aurez besoin de tester les méthodes d'une classe isolément de ses dépendances,
  • Là où l'incertitude de la durée de vie d'une dépendance peut justifier une expérimentation (par exemple, Hey, MyDepClassest thread safe - que faire si nous en faisons un singleton et injectons la même instance à tous les consommateurs?)

Exemple

Voici une implémentation C # simple. Étant donné la classe de consommation ci-dessous:

public class MyLogger
{
   public void LogRecord(string somethingToLog)
   {
      Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
   }
}

Bien qu'apparemment inoffensif, il a deux staticdépendances sur deux autres classes System.DateTimeet System.Console, ce qui limite non seulement les options de sortie de journalisation (la journalisation sur la console ne vaudra rien si personne ne regarde), mais pire, il est difficile de tester automatiquement étant donné la dépendance à une horloge système non déterministe.

On peut cependant s’appliquer DIPà cette classe, en faisant abstraction du souci de l’horodatage comme dépendance, et en MyLoggerne le couplant qu’à une interface simple:

public interface IClock
{
    DateTime Now { get; }
}

Nous pouvons également relâcher la dépendance à l'égard Consoled'une abstraction, telle que a TextWriter. L'injection de dépendance est généralement implémentée sous forme d' constructorinjection (passage d'une abstraction à une dépendance en tant que paramètre au constructeur d'une classe consommatrice) ou Setter Injection(passage de la dépendance via un setXyz()setter ou une propriété .Net avec {set;}défini). L'injection de constructeur est préférable, car cela garantit que la classe sera dans un état correct après la construction et permet aux champs de dépendance internes d'être marqués comme readonly(C #) ou final(Java). Donc, en utilisant l'injection de constructeur dans l'exemple ci-dessus, cela nous laisse:

public class MyLogger : ILogger // Others will depend on our logger.
{
    private readonly TextWriter _output;
    private readonly IClock _clock;

    // Dependencies are injected through the constructor
    public MyLogger(TextWriter stream, IClock clock)
    {
        _output = stream;
        _clock = clock;
    }

    public void LogRecord(string somethingToLog)
    {
        // We can now use our dependencies through the abstraction 
        // and without knowledge of the lifespans of the dependencies
        _output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog);
    }
}

(Un béton Clockdoit être fourni, ce qui pourrait bien sûr revenir DateTime.Now, et les deux dépendances doivent être fournies par un conteneur IoC via l'injection de constructeur)

Un test unitaire automatisé peut être construit, ce qui prouve définitivement que notre enregistreur fonctionne correctement, car nous avons maintenant le contrôle sur les dépendances - le temps, et nous pouvons espionner la sortie écrite:

[Test]
public void LoggingMustRecordAllInformationAndStampTheTime()
{
    // Arrange
    var mockClock = new Mock<IClock>();
    mockClock.Setup(c => c.Now).Returns(new DateTime(2015, 4, 11, 12, 31, 45));
    var fakeConsole = new StringWriter();

    // Act
    new MyLogger(fakeConsole, mockClock.Object)
        .LogRecord("Foo");

    // Assert
    Assert.AreEqual("2015-04-11 12:31:45 - Foo", fakeConsole.ToString());
}

Prochaines étapes

L'injection de dépendances est invariablement associée à un conteneur d'inversion de contrôle (IoC) , pour injecter (fournir) les instances de dépendance concrètes et pour gérer les instances de durée de vie. Pendant le processus de configuration / amorçage, les IoCconteneurs permettent de définir les éléments suivants:

  • mappage entre chaque abstraction et l'implémentation concrète configurée (par exemple "chaque fois qu'un consommateur en fait la demande IBar, retourne une ConcreteBarinstance" )
  • des stratégies peuvent être définies pour la gestion de la durée de vie de chaque dépendance, par exemple pour créer un nouvel objet pour chaque instance de consommateur, pour partager une instance de dépendance singleton entre tous les consommateurs, pour partager la même instance de dépendance uniquement sur le même thread, etc.
  • Dans .Net, les conteneurs IoC connaissent les protocoles tels que IDisposableet prendront la responsabilité des Disposingdépendances conformément à la gestion de la durée de vie configurée.

En règle générale, une fois les conteneurs IoC configurés / démarrés, ils fonctionnent de manière transparente en arrière-plan, ce qui permet au codeur de se concentrer sur le code à la main plutôt que de se soucier des dépendances.

La clé du code convivial DI est d'éviter le couplage statique des classes et de ne pas utiliser new () pour la création de dépendances

Comme dans l'exemple ci-dessus, le découplage des dépendances nécessite un certain effort de conception, et pour le développeur, il y a un changement de paradigme nécessaire pour briser l'habitude d' newingérer des dépendances directement et de faire plutôt confiance au conteneur pour gérer les dépendances.

Mais les avantages sont nombreux, en particulier dans la possibilité de tester minutieusement votre classe d'intérêt.

Remarque : La création / mappage / projection (via new ..()) de POCO / POJO / DTO de sérialisation / Graphes d'entité / Projections JSON anonymes et autres - c'est-à-dire les classes ou enregistrements "Données uniquement" - utilisés ou renvoyés par les méthodes ne sont pas considérés comme des dépendances (dans le Sens UML) et non soumis à DI. Les newprojeter est très bien.


1
Le problème est DIP! = DI. Le DIP consiste à découpler l'abstraction de la mise en œuvre: A. Les modules de haut niveau ne devraient pas dépendre de modules de bas niveau. Les deux devraient dépendre d'abstractions. B. Les abstractions ne devraient pas dépendre des détails. Les détails doivent dépendre des abstractions. DI est un moyen de dissocier la création d'objets de l'utilisation d'objets.
Ricardo Rivaldo

Oui, la distinction est clairement énoncée dans mon paragraphe 2, "DI l'une des implémentations possibles de DIP" , dans le paradigme SOLIDE d'oncle Bob. J'ai également précisé cela dans un article précédent.
StuartLC

25

L'intérêt de l'Injection de dépendances (DI) est de maintenir le code source de l'application propre et stable :

  • nettoyer le code d'initialisation des dépendances
  • stable quelle que soit la dépendance utilisée

Pratiquement, chaque modèle de conception sépare les préoccupations pour que les modifications futures affectent les fichiers minimum.

Le domaine spécifique de DI est la délégation de la configuration et de l'initialisation des dépendances.

Exemple: DI avec script shell

Si vous travaillez occasionnellement en dehors de Java, rappelez-vous comment il sourceest souvent utilisé dans de nombreux langages de script (Shell, Tcl, etc., ou même importen Python mal utilisé à cet effet).

Considérez un dependent.shscript simple :

#!/bin/sh
# Dependent
touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

Le script est dépendant: il ne s'exécutera pas correctement seul ( archive_filesn'est pas défini).

Vous définissez archive_filesdans le archive_files_zip.shscript d'implémentation (en utilisant zipdans ce cas):

#!/bin/sh
# Dependency
function archive_files {
    zip files.zip "$@"
}

Au lieu de source-ing directement le script d'implémentation dans le script dépendant, vous utilisez un injector.sh"conteneur" qui enveloppe les deux "composants":

#!/bin/sh 
# Injector
source ./archive_files_zip.sh
source ./dependent.sh

La archive_files dépendance vient d'être injectée dans un script dépendant .

Vous pourriez avoir injecté une dépendance qui implémente l' archive_filesutilisation de tarou xz.

Exemple: suppression de DI

Si le dependent.shscript utilisait directement les dépendances, l'approche serait appelée recherche de dépendance (qui est opposée à l' injection de dépendance ):

#!/bin/sh
# Dependent

# dependency look-up
source ./archive_files_zip.sh

touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

Maintenant, le problème est que le "composant" dépendant doit effectuer lui-même l'initialisation.

Le code source du "composant" n'est ni propre ni stable car chaque changement dans l'initialisation des dépendances nécessite également une nouvelle version du fichier de code source des "composants".

Derniers mots

DI n'est pas aussi largement souligné et popularisé que dans les frameworks Java.

Mais c'est une approche générique pour diviser les préoccupations de:

  • développement d' applications ( cycle de vie de publication de code source unique )
  • déploiement d' applications ( plusieurs environnements cibles avec des cycles de vie indépendants)

L'utilisation de la configuration uniquement avec la recherche de dépendances n'aide pas car le nombre de paramètres de configuration peut changer par dépendance (par exemple, nouveau type d'authentification) ainsi que le nombre de types de dépendances pris en charge (par exemple, nouveau type de base de données).


J'ajouterais la possibilité de terminer une classe particulière (test) sans avoir à compléter ses dépendances, comme un objectif pour DI.
David

22

Toutes les réponses ci-dessus sont bonnes, mon objectif est d'expliquer le concept de manière simple afin que toute personne sans connaissances en programmation puisse également comprendre le concept

L'injection de dépendance est l'un des modèles de conception qui nous aident à créer des systèmes complexes de manière plus simple.

Nous pouvons voir une grande variété d'applications de ce modèle dans notre vie quotidienne. Certains des exemples sont le magnétophone, le VCD, le lecteur de CD, etc.

Magnétophone portable bobine à bobine, milieu du 20e siècle.

L'image ci-dessus est une image d'un magnétophone portable à bobine, milieu du 20e siècle. Source .

L'intention première d'un magnétophone est d'enregistrer ou de lire du son.

Lors de la conception d'un système, il faut une bobine pour enregistrer ou lire du son ou de la musique. Il existe deux possibilités pour concevoir ce système

  1. nous pouvons placer la bobine à l'intérieur de la machine
  2. nous pouvons fournir un crochet pour la bobine où il peut être placé.

Si nous utilisons le premier, nous devons ouvrir la machine pour changer la bobine. si nous optons pour le second, qui consiste à placer un crochet pour bobine, nous obtenons un avantage supplémentaire de jouer de la musique en changeant la bobine. et aussi de réduire la fonction uniquement à jouer quoi que ce soit dans la bobine.

Comme pour l'injection de dépendances, le processus d'externalisation des dépendances se concentre uniquement sur la fonctionnalité spécifique du composant afin que les composants indépendants puissent être couplés ensemble pour former un système complexe.

Les principaux avantages que nous avons obtenus en utilisant l'injection de dépendance.

  • Haute cohésion et couplage lâche.
  • Externaliser la dépendance et ne regarder que la responsabilité.
  • Faire des choses en tant que composants et combiner pour former un grand système avec des capacités élevées.
  • Il aide à développer des composants de haute qualité car ils sont développés indépendamment, ils sont correctement testés.
  • Il est utile de remplacer le composant par un autre en cas de défaillance.

Aujourd'hui, ce concept forme la base de cadres bien connus dans le monde de la programmation. Les Spring Angular etc sont les frameworks logiciels bien connus construits au sommet de ce concept

L'injection de dépendance est un modèle utilisé pour créer des instances d'objets sur lesquels d'autres objets s'appuient sans savoir au moment de la compilation quelle classe sera utilisée pour fournir cette fonctionnalité ou simplement la manière d'injecter des propriétés à un objet est appelée injection de dépendance.

Exemple d'injection de dépendance

Auparavant, nous écrivons du code comme celui-ci

Public MyClass{
 DependentClass dependentObject
 /*
  At somewhere in our code we need to instantiate 
  the object with new operator  inorder to use it or perform some method.
  */ 
  dependentObject= new DependentClass();
  dependentObject.someMethod();
}

Avec l'injection de dépendance, l'injecteur de dépendance décollera l'instanciation pour nous

Public MyClass{
 /* Dependency injector will instantiate object*/
 DependentClass dependentObject

 /*
  At somewhere in our code we perform some method. 
  The process of  instantiation will be handled by the dependency injector
 */ 

  dependentObject.someMethod();
}

Vous pouvez également lire

Différence entre l'inversion du contrôle et l'injection de dépendance


17

Qu'est-ce que l'injection de dépendance?

L'injection de dépendance (DI) signifie découpler les objets qui dépendent les uns des autres. Supposons que l'objet A dépend de l'objet B, l'idée est donc de découpler ces objets les uns des autres. Nous n'avons pas besoin de coder en dur l'objet en utilisant un nouveau mot clé plutôt que de partager les dépendances avec les objets au moment de l'exécution malgré le temps de compilation. Si on parle de

Comment fonctionne l'injection de dépendance au printemps:

Nous n'avons pas besoin de coder en dur l'objet en utilisant un nouveau mot clé, plutôt de définir la dépendance du bean dans le fichier de configuration. Le conteneur à ressort sera chargé de tout brancher.

Inversion de contrôle (IOC)

Le CIO est un concept général et il peut être exprimé de différentes manières et l'injection de dépendance est un exemple concret du CIO.

Deux types d'injection de dépendance:

  1. Injection de constructeur
  2. Setter Injection

1. Injection de dépendance basée sur le constructeur:

L'ID basé sur constructeur est accompli lorsque le conteneur invoque un constructeur de classe avec un certain nombre d'arguments, chacun représentant une dépendance à l'égard d'une autre classe.

public class Triangle {

private String type;

public String getType(){
    return type;
 }

public Triangle(String type){   //constructor injection
    this.type=type;
 }
}
<bean id=triangle" class ="com.test.dependencyInjection.Triangle">
        <constructor-arg value="20"/>
  </bean>

2. Injection de dépendance basée sur Setter:

L'ID basé sur Setter est accompli par le conteneur appelant des méthodes de définition sur vos beans après avoir appelé un constructeur sans argument ou une méthode d'usine statique sans argument pour instancier votre bean.

public class Triangle{

 private String type;

 public String getType(){
    return type;
  }
 public void setType(String type){          //setter injection
    this.type = type;
  }
 }

<!-- setter injection -->
 <bean id="triangle" class="com.test.dependencyInjection.Triangle">
        <property name="type" value="equivialteral"/>

REMARQUE: il s'agit d'une bonne règle générale d'utiliser des arguments de constructeur pour les dépendances obligatoires et des paramètres pour les dépendances facultatives. Notez que si nous utilisons une annotation basée sur @Required, une annotation sur un setter peut être utilisée pour faire des setters une dépendance requise.


15

La meilleure analogie à laquelle je peux penser est le chirurgien et ses assistants dans une salle d'opération, où le chirurgien est la personne principale et son assistant qui fournit les divers composants chirurgicaux quand il en a besoin afin que le chirurgien puisse se concentrer sur celui-ci. chose qu'il fait le mieux (chirurgie). Sans l'assistant, le chirurgien doit obtenir lui-même les composants chaque fois qu'il en a besoin.

DI pour faire court, est une technique pour supprimer une responsabilité supplémentaire commune (fardeau) sur les composants pour récupérer les composants dépendants, en les leur fournissant.

DI vous rapproche du principe de responsabilité unique (SR), comme le surgeon who can concentrate on surgery.

Quand utiliser DI: Je recommanderais d'utiliser DI dans presque tous les projets de production (petits / grands), en particulier dans des environnements commerciaux en constante évolution :)

Pourquoi: parce que vous voulez que votre code soit facilement testable, mockable, etc. afin que vous puissiez tester rapidement vos modifications et le mettre sur le marché. En outre, pourquoi ne le feriez-vous pas lorsque vous disposez de nombreux outils / cadres gratuits impressionnants pour vous aider dans votre voyage vers une base de code où vous avez plus de contrôle.


@WindRider Merci. Je ne peux pas être plus d'accord. La vie humaine et le corps humain sont de magnifiques exemples d'excellence de conception ... la colonne vertébrale est un excellent exemple d'un ESB:) ...
Anwar Husain

15

Exemple, nous avons 2 classes Clientet Service. ClientutiliseraService

public class Service {
    public void doSomeThingInService() {
        // ...
    }
}

Sans injection de dépendance

Voie 1)

public class Client {
    public void doSomeThingInClient() {
        Service service = new Service();
        service.doSomeThingInService();
    }
}

Voie 2)

public class Client {
    Service service = new Service();
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

Voie 3)

public class Client {
    Service service;
    public Client() {
        service = new Service();
    }
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

1) 2) 3) Utilisation

Client client = new Client();
client.doSomeThingInService();

Les avantages

  • Facile

Désavantages

  • Difficile pour la Clientclasse de test
  • Lorsque nous changeons de Serviceconstructeur, nous devons changer le code partout créer un Serviceobjet

Utiliser l'injection de dépendance

Voie 1) Injection de constructeur

public class Client {
    Service service;

    Client(Service service) {
        this.service = service;
    }

    // Example Client has 2 dependency 
    // Client(Service service, IDatabas database) {
    //    this.service = service;
    //    this.database = database;
    // }

    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

En utilisant

Client client = new Client(new Service());
// Client client = new Client(new Service(), new SqliteDatabase());
client.doSomeThingInClient();

Voie 2) Setter injection

public class Client {
    Service service;

    public void setService(Service service) {
        this.service = service;
    }

    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

En utilisant

Client client = new Client();
client.setService(new Service());
client.doSomeThingInClient();

Voie 3) Injection d'interface

Vérifiez https://en.wikipedia.org/wiki/Dependency_injection

===

Maintenant, ce code est déjà suivi Dependency Injectionet il est plus facile pour la Clientclasse de test .
Cependant, nous utilisons encore new Service()beaucoup de temps et ce n'est pas bon quand on change de Serviceconstructeur. Pour l'empêcher, nous pouvons utiliser un injecteur DI comme
1) Manuel simpleInjector

public class Injector {
    public static Service provideService(){
        return new Service();
    }

    public static IDatabase provideDatatBase(){
        return new SqliteDatabase();
    }
    public static ObjectA provideObjectA(){
        return new ObjectA(provideService(...));
    }
}

En utilisant

Service service = Injector.provideService();

2) Utiliser la bibliothèque: pour Android dagger2

Les avantages

  • Rendre le test plus facile
  • Lorsque vous modifiez le Service, il vous suffit de le modifier dans la classe Injector
  • Si vous utilisez use Constructor Injection, lorsque vous regardez le constructeur de Client, vous verrez combien de dépendances de Clientclasse

Désavantages

  • Si vous utilisez use Constructor Injection, l' Serviceobjet est créé lors de sa Clientcréation, parfois nous utilisons la fonction en Clientclasse sans utilisation Servicedonc la création Serviceest gaspillée

Définition de l'injection de dépendance

https://en.wikipedia.org/wiki/Dependency_injection

Une dépendance est un objet qui peut être utilisé ( Service)
Une injection est le passage d'une dépendance ( Service) à un objet dépendant ( Client) qui l'utiliserait


13

Cela signifie que les objets ne devraient avoir que autant de dépendances que nécessaire pour faire leur travail et que les dépendances devraient être peu nombreuses. De plus, les dépendances d'un objet devraient être sur des interfaces et non sur des objets «concrets», lorsque cela est possible. (Un objet concret est tout objet créé avec le mot-clé new.) Le couplage lâche favorise une plus grande réutilisabilité, une facilité de maintenance plus facile et vous permet de fournir facilement des objets «fictifs» au lieu de services coûteux.

La «Dependency Injection» (DI) est également connue comme «Inversion de contrôle» (IoC), peut être utilisée comme une technique pour encourager ce couplage lâche.

Il existe deux approches principales pour mettre en œuvre l'ID:

  1. Injection constructeur
  2. Setter injection

Injection constructeur

C'est la technique de passage des dépendances d'objets à son constructeur.

Notez que le constructeur accepte une interface et non un objet concret. Notez également qu'une exception est levée si le paramètre orderDao est null. Cela souligne l'importance de recevoir une dépendance valide. L'injection de constructeur est, à mon avis, le mécanisme préféré pour donner à un objet ses dépendances. Il est clair pour le développeur lors de l'appel de l'objet quelles dépendances doivent être attribuées à l'objet "Personne" pour une exécution correcte.

Setter Injection

Mais considérons l'exemple suivant… Supposons que vous ayez une classe avec dix méthodes qui n'ont pas de dépendances, mais que vous ajoutez une nouvelle méthode qui a une dépendance sur IDAO. Vous pouvez changer le constructeur pour utiliser l'injection de constructeur, mais cela peut vous obliger à modifier tous les appels de constructeur partout. Alternativement, vous pouvez simplement ajouter un nouveau constructeur qui prend la dépendance, mais comment un développeur sait-il facilement quand utiliser un constructeur par rapport à l'autre? Enfin, si la dépendance est très coûteuse à créer, pourquoi devrait-elle être créée et transmise au constructeur alors qu'elle ne peut être utilisée que rarement? "Setter Injection" est une autre technique DI qui peut être utilisée dans des situations comme celle-ci.

Setter Injection ne force pas les dépendances à passer au constructeur. Au lieu de cela, les dépendances sont définies sur des propriétés publiques exposées par l'objet dans le besoin. Comme indiqué précédemment, les principaux facteurs de motivation pour ce faire comprennent:

  1. Prise en charge de l'injection de dépendance sans avoir à modifier le constructeur d'une classe héritée.
  2. Permettre la création de ressources ou de services coûteux le plus tard possible et uniquement en cas de besoin.

Voici l'exemple à quoi ressemblerait le code ci-dessus:

public class Person {
    public Person() {}

    public IDAO Address {
        set { addressdao = value; }
        get {
            if (addressdao == null)
              throw new MemberAccessException("addressdao" +
                             " has not been initialized");
            return addressdao;
        }
    }

    public Address GetAddress() {
       // ... code that uses the addressdao object
       // to fetch address details from the datasource ...
    }

    // Should not be called directly;
    // use the public property instead
    private IDAO addressdao;

3
Je pense que votre premier paragraphe s'éloigne de la question, et n'est pas du tout la définition de DI (c'est-à-dire que vous essayez de définir SOLIDE, pas DI). Techniquement, même si vous avez 100 dépendances, vous pouvez toujours utiliser l'injection de dépendance. De même, il est possible d'injecter des dépendances concrètes - il s'agit toujours d'une injection de dépendances.
Jay Sullivan

10

Je pense que puisque tout le monde a écrit pour DI, permettez-moi de poser quelques questions ..

  1. Quand vous avez une configuration de DI où toutes les implémentations réelles (pas les interfaces) qui vont être injectées dans une classe (par exemple des services à un contrôleur) pourquoi n'est-ce pas une sorte de codage en dur?
  2. Que faire si je souhaite modifier l'objet au moment de l'exécution? Par exemple, ma configuration indique déjà lorsque j'instancie MyController, injectez pour FileLogger en tant qu'ILogger. Mais je pourrais vouloir injecter DatabaseLogger.
  3. Chaque fois que je veux changer les objets dont mon AClass a besoin, je dois maintenant regarder dans deux endroits - La classe elle-même et le fichier de configuration. Comment cela vous facilite-t-il la vie?
  4. Si Aproperty of AClass n'est pas injecté, est-il plus difficile de se moquer de lui?
  5. Revenons à la première question. Si l'utilisation de new object () est mauvaise, comment se fait-il que nous injections l'implémentation et non l'interface? Je pense que beaucoup d'entre vous disent que nous injectons en fait l'interface mais la configuration vous fait spécifier l'implémentation de cette interface .. pas au moment de l'exécution .. elle est codée en dur pendant la compilation.

Ceci est basé sur la réponse @Adam N publiée.

Pourquoi PersonService n'a plus à se soucier de GroupMembershipService? Vous venez de mentionner que GroupMembership a plusieurs choses (objets / propriétés) dont cela dépend. Si GMService était requis dans PService, vous l'auriez comme propriété. Vous pouvez vous moquer de cela, que vous l'ayez ou non injecté. La seule fois où j'aimerais qu'il soit injecté, c'est si GMService avait des classes enfants plus spécifiques, que vous ne sauriez pas avant l'exécution. Ensuite, vous voudriez injecter la sous-classe. Ou si vous vouliez l'utiliser comme singleton ou comme prototype. Pour être honnête, le fichier de configuration a tout codé en dur en ce qui concerne la sous-classe pour un type (interface) qu'il va injecter pendant la compilation.

ÉDITER

Un joli commentaire de Jose Maria Arranz sur DI

DI augmente la cohésion en supprimant tout besoin de déterminer le sens de la dépendance et d'écrire n'importe quel code de colle.

Faux. La direction des dépendances est sous forme XML ou sous forme d'annotations, vos dépendances sont écrites sous forme de code XML et d'annotations. XML et les annotations SONT du code source.

DI réduit le couplage en rendant tous vos composants modulaires (c'est-à-dire remplaçables) et ont des interfaces bien définies les unes aux autres.

Faux. Vous n'avez pas besoin d'un framework DI pour construire un code modulaire basé sur des interfaces.

À propos du remplaçable: avec une archive .properties très simple et Class.forName, vous pouvez définir quelles classes peuvent changer. Si N'IMPORTE QUELLE classe de votre code peut être modifiée, Java n'est pas pour vous, utilisez un langage de script. Soit dit en passant: les annotations ne peuvent pas être modifiées sans recompilation.

À mon avis, il n'y a qu'une seule raison pour les cadres DI: la réduction de la plaque de la chaudière. Avec un système d'usine bien fait, vous pouvez faire la même chose, plus contrôlé et plus prévisible que votre infrastructure DI préférée, les infrastructures DI promettent une réduction de code (XML et les annotations sont aussi du code source). Le problème est que cette réduction de la plaque de chaudière est juste réelle dans des cas très très simples (une instance par classe et similaire), parfois dans le monde réel, choisir l'objet de service approprié n'est pas aussi facile que de mapper une classe à un objet singleton.


8

Les réponses populaires sont inutiles, car elles définissent l'injection de dépendance d'une manière qui n'est pas utile. Convenons que par «dépendance», nous entendons un autre objet préexistant dont notre objet X a besoin. Mais nous ne disons pas que nous faisons "l'injection de dépendance" quand nous disons

$foo = Foo->new($bar);

Nous appelons simplement cela le passage de paramètres dans le constructeur. Nous le faisons régulièrement depuis que les constructeurs ont été inventés.

"L'injection de dépendance" est considérée comme un type "d'inversion de contrôle", ce qui signifie qu'une partie de la logique est supprimée de l'appelant. Ce n'est pas le cas lorsque l'appelant passe des paramètres, donc si c'était DI, DI n'impliquerait pas une inversion de contrôle.

DI signifie qu'il existe un niveau intermédiaire entre l'appelant et le constructeur qui gère les dépendances. Un Makefile est un exemple simple d'injection de dépendances. L '"appelant" est la personne tapant "make bar" sur la ligne de commande, et le "constructeur" est le compilateur. Le Makefile spécifie que la barre dépend de foo, et il fait un

gcc -c foo.cpp; gcc -c bar.cpp

avant de faire un

gcc foo.o bar.o -o bar

La personne qui tape "make bar" n'a pas besoin de savoir que la barre dépend de foo. La dépendance a été injectée entre "make bar" et gcc.

Le but principal du niveau intermédiaire n'est pas seulement de passer les dépendances au constructeur, mais de lister toutes les dépendances en un seul endroit , et de les cacher au codeur (et non de les faire fournir au codeur).

Habituellement, le niveau intermédiaire fournit des usines pour les objets construits, qui doivent fournir un rôle que chaque type d'objet demandé doit remplir. C'est parce qu'en ayant un niveau intermédiaire qui cache les détails de la construction, vous avez déjà encouru la pénalité d'abstraction imposée par les usines, vous pourriez donc aussi bien utiliser des usines.


8

L'injection de dépendance signifie un moyen (en fait de toute façon ) pour une partie du code (par exemple une classe) d'avoir accès aux dépendances (d'autres parties du code, par exemple d'autres classes, cela dépend) de manière modulaire sans qu'elles soient codées en dur (donc ils peuvent changer ou être remplacés librement, ou même être chargés à un autre moment, selon les besoins)

(et ps, oui c'est devenu un nom trop hype de 25 $ pour un concept assez simple) , mes .25centimes


8

Je sais qu'il existe déjà de nombreuses réponses, mais j'ai trouvé cela très utile: http://tutorials.jenkov.com/dependency-injection/index.html

Aucune dépendance:

public class MyDao {

  protected DataSource dataSource = new DataSourceImpl(
    "driver", "url", "user", "password");

  //data access methods...
  public Person readPerson(int primaryKey) {...}     
}

Dépendance:

public class MyDao {

  protected DataSource dataSource = null;

  public MyDao(String driver, String url, String user, String password) {
    this.dataSource = new DataSourceImpl(driver, url, user, password);
  }

  //data access methods...
  public Person readPerson(int primaryKey) {...}
}

Remarquez comment l' DataSourceImplinstanciation est déplacée dans un constructeur. Le constructeur prend quatre paramètres qui sont les quatre valeurs requises par le DataSourceImpl. Bien que la MyDaoclasse dépend toujours de ces quatre valeurs, elle ne satisfait plus ces dépendances elle-même. Ils sont fournis par la classe qui crée une MyDaoinstance.


1
DI ne vous passerait-il pas par l'interface de votre DataSourceImp déjà construit?
PmanAce

6

L'injection de dépendance est une solution possible à ce que l'on pourrait généralement appeler l'exigence d '«obscurcissement de la dépendance». L'obfuscation de la dépendance est une méthode pour retirer la nature «évidente» du processus de fourniture d'une dépendance à une classe qui l'exige et donc d'obscurcir, en quelque sorte, la fourniture de ladite dépendance à ladite classe. Ce n'est pas forcément une mauvaise chose. En fait, en obscurcissant la manière dont une dépendance est fournie à une classe, alors quelque chose en dehors de la classe est responsable de la création de la dépendance, ce qui signifie que, dans divers scénarios, une implémentation différente de la dépendance peut être fournie à la classe sans apporter de modifications. à la classe. Ceci est idéal pour basculer entre les modes de production et de test (par exemple, en utilisant une dépendance de service «factice»).

Malheureusement, la mauvaise partie est que certaines personnes ont supposé que vous aviez besoin d'un cadre spécialisé pour faire de l'obscurcissement des dépendances et que vous êtes en quelque sorte un programmeur `` moindre '' si vous choisissez de ne pas utiliser un cadre particulier pour le faire. Un autre mythe extrêmement troublant, que beaucoup croient, est que l'injection de dépendance est le seul moyen d'atteindre l'obscurcissement de la dépendance. C'est manifestement et historiquement et évidemment 100% faux, mais vous aurez du mal à convaincre certaines personnes qu'il existe des alternatives à l'injection de dépendance pour vos besoins d'obscurcissement de la dépendance.

Les programmeurs ont compris l'exigence d'obscurcissement de la dépendance depuis des années et de nombreuses solutions alternatives ont évolué avant et après la conception de l'injection de dépendance. Il existe des modèles Factory, mais il existe également de nombreuses options utilisant ThreadLocal où aucune injection dans une instance particulière n'est nécessaire - la dépendance est effectivement injectée dans le thread, ce qui a l'avantage de rendre l'objet disponible (via des méthodes getter statiques pratiques) pour toutclasse qui en a besoin sans avoir à ajouter des annotations aux classes qui en ont besoin et à mettre en place une «colle» XML complexe pour y arriver. Lorsque vos dépendances sont requises pour la persistance (JPA / JDO ou autre), cela vous permet d'atteindre la `` persistance transparente '' beaucoup plus facilement et avec des modèles de domaine et des classes de modèles commerciaux constitués uniquement de POJO (c'est-à-dire pas de framework spécifique / verrouillés dans des annotations).



5

Avant de passer à la description technique, visualisez-le d'abord avec un exemple réel, car vous trouverez beaucoup de choses techniques pour apprendre l'injection de dépendance, mais le temps maximum que des gens comme moi ne peuvent pas obtenir le concept de base.

Dans la première image, supposons que vous avez une usine automobile avec beaucoup d'unités. Une voiture est en fait construite dans l' unité d'assemblage mais elle a besoin de moteur , de sièges ainsi que de roues . L' unité d'assemblage dépend donc de toutes ces unités et ce sont les dépendances de l'usine.

Vous pouvez sentir que maintenant il est trop compliqué de maintenir toutes les tâches dans cette usine car avec la tâche principale (Assembler la voiture dans l'unité d'assemblage), vous devez également vous concentrer sur d' autres unités . Il est maintenant très coûteux à entretenir et le bâtiment de l'usine est immense, il vous faut donc louer votre argent supplémentaire.

Maintenant, regardez la deuxième image. Si vous trouvez des fournisseurs qui vous fourniront des roues , des sièges et des moteurs moins chers que vos coûts d'autoproduction, vous n'avez plus besoin de les fabriquer dans votre usine. Vous pouvez louer un bâtiment plus petit maintenant juste pour votre unité de montage, ce qui réduira votre tâche d'entretien et réduira vos coûts de location supplémentaires. Maintenant, vous pouvez également vous concentrer uniquement sur votre tâche principale (assemblage de voitures).

Maintenant, nous pouvons dire que toutes les dépendances pour assembler une voiture sont injectées en usine par les fournisseurs . Il s'agit d'un exemple d' injection de dépendance (DI) réelle .

Maintenant, dans le mot technique, l'injection de dépendances est une technique par laquelle un objet (ou méthode statique) fournit les dépendances d'un autre objet. Ainsi, le transfert de la tâche de créer l'objet à quelqu'un d'autre et d'utiliser directement la dépendance est appelé injection de dépendance.

Cela vous aidera maintenant à apprendre DI avec un mot technique. Cela montrera quand utiliser DI et quand ne devrait pas .

Tout dans une seule usine automobile.

Usine de voiture simple


1
réponse la plus claire sur les 40 ish. Exemple et images de la vie réelle. +1. Doit être la réponse acceptée.
Marche Remi

4

de Book Apress.Spring.Persistence.with.Hibernate.Oct.2010

Le but de l'injection de dépendances est de découpler le travail de résolution des composants logiciels externes de la logique métier de votre application. Cela augmente non seulement le potentiel d'erreurs, ajoute du gonflement de code et amplifie les complexités de maintenance; il couple les composants plus étroitement ensemble, ce qui rend difficile la modification des dépendances lors de la refactorisation ou des tests.


4

L'injection de dépendance (DI) est celle de Design Patterns, qui utilise la fonction de base de la POO - la relation entre un objet et un autre objet. Alors que l'héritage hérite d'un objet pour faire un autre objet plus complexe et spécifique, la relation ou l'association crée simplement un pointeur vers un autre objet à partir d'un objet à l'aide d'un attribut. La puissance de DI est combinée avec d'autres fonctionnalités de la POO, tout comme les interfaces et le code masqué. Supposons que nous ayons un client (abonné) dans la bibliothèque, qui ne peut emprunter qu'un seul livre pour plus de simplicité.

Interface du livre:

package com.deepam.hidden;

public interface BookInterface {

public BookInterface setHeight(int height);
public BookInterface setPages(int pages);   
public int getHeight();
public int getPages();  

public String toString();
}

Ensuite, nous pouvons avoir plusieurs types de livres; l'un des types est la fiction:

package com.deepam.hidden;

public class FictionBook implements BookInterface {
int height = 0; // height in cm
int pages = 0; // number of pages

/** constructor */
public FictionBook() {
    // TODO Auto-generated constructor stub
}

@Override
public FictionBook setHeight(int height) {
  this.height = height;
  return this;
}

@Override
public FictionBook setPages(int pages) {
  this.pages = pages;
  return this;      
}

@Override
public int getHeight() {
    // TODO Auto-generated method stub
    return height;
}

@Override
public int getPages() {
    // TODO Auto-generated method stub
    return pages;
}

@Override
public String toString(){
    return ("height: " + height + ", " + "pages: " + pages);
}
}

Désormais, l'abonné peut être associé au livre:

package com.deepam.hidden;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Subscriber {
BookInterface book;

/** constructor*/
public Subscriber() {
    // TODO Auto-generated constructor stub
}

// injection I
public void setBook(BookInterface book) {
    this.book = book;
}

// injection II
public BookInterface setBook(String bookName) {
    try {
        Class<?> cl = Class.forName(bookName);
        Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor
        BookInterface book = (BookInterface) constructor.newInstance();
        //book = (BookInterface) Class.forName(bookName).newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    return book;
}

public BookInterface getBook() {
  return book;
}

public static void main(String[] args) {

}

}

Les trois classes peuvent être masquées pour sa propre implémentation. Maintenant, nous pouvons utiliser ce code pour DI:

package com.deepam.implement;

import com.deepam.hidden.Subscriber;
import com.deepam.hidden.FictionBook;

public class CallHiddenImplBook {

public CallHiddenImplBook() {
    // TODO Auto-generated constructor stub
}

public void doIt() {
    Subscriber ab = new Subscriber();

    // injection I
    FictionBook bookI = new FictionBook();
    bookI.setHeight(30); // cm
    bookI.setPages(250);
    ab.setBook(bookI); // inject
    System.out.println("injection I " + ab.getBook().toString());

    // injection II
    FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set
    System.out.println("injection II " + ab.getBook().toString());      
}

public static void main(String[] args) {
    CallHiddenImplBook kh = new CallHiddenImplBook();
    kh.doIt();
}
}

Il existe de nombreuses façons d'utiliser l'injection de dépendance. Il est possible de le combiner avec Singleton, etc., mais toujours en base ce n'est qu'une association réalisée en créant un attribut de type d'objet à l'intérieur d'un autre objet. L'utilité est seulement et seulement dans la fonctionnalité, ce code, que nous devons écrire encore et encore, est toujours préparé et fait pour nous. C'est pourquoi DI est si étroitement lié à Inversion of Control (IoC), ce qui signifie que notre programme passe le contrôle d'un autre module en cours d'exécution, qui effectue des injections de beans dans notre code. (Chaque objet, qui peut être injecté, peut être signé ou considéré comme un bean.) Par exemple, au printemps, cela se fait en créant et en initialisant ApplicationContextconteneur, qui fait ce travail pour nous. Nous créons simplement dans notre code le contexte et invoquons l'initialisation des beans. À ce moment, l'injection a été effectuée automatiquement.


4

Injection de dépendance pour les enfants de 5 ans.

Lorsque vous sortez du réfrigérateur par vous-même, vous pouvez causer des problèmes. Vous pourriez laisser la porte ouverte, vous pourriez obtenir quelque chose que maman ou papa ne veut pas que vous ayez. Vous cherchez peut-être même quelque chose que nous n'avons même pas ou qui a expiré.

Ce que vous devriez faire, c'est énoncer un besoin: «J'ai besoin de quelque chose à boire avec le déjeuner», puis nous nous assurerons que vous avez quelque chose lorsque vous vous asseyez pour manger.


1
C'est clairement la réponse d'un parent. ;)
Marche Remi

4

Extrait de Christoffer Noring, livre de Pablo Deeleman «Learning Angular - Second Edition»:

"Au fur et à mesure que nos applications se développent et évoluent, chacune de nos entités de code nécessitera en interne des instances d'autres objets , mieux connus sous le nom de dépendances dans le monde de l'ingénierie logicielle. L' action de transmettre ces dépendances au client dépendant est appelée injection , et cela implique également la participation d'une autre entité de code, appelée l' injecteur . L' injecteur se chargera d' instancier et d' amorcer les dépendances requisesils sont donc prêts à l'emploi dès le moment où ils sont injectés avec succès dans le client. Ceci est très important car le client ne sait rien sur la façon d' instancier ses propres dépendances et ne connaît que l' interface qu'il implémente pour les utiliser. "

De: Anton Moiseev. livre "Angular Development with Typescript, Second Edition.":

«En bref, DI vous aide à écrire du code de manière peu couplée et rend votre code plus testable et réutilisable


3

En termes simples, l'injection de dépendance (DI) est le moyen de supprimer les dépendances ou le couplage étroit entre différents objets. L'injection de dépendance donne un comportement cohérent à chaque objet.

DI est la mise en œuvre du principal du CIO du printemps qui dit "Ne nous appelez pas, nous vous appellerons". L'utilisation du programmeur d'injection de dépendance n'a pas besoin de créer d'objet à l'aide du nouveau mot clé.

Les objets sont une fois chargés dans le conteneur Spring, puis nous les réutilisons chaque fois que nous en avons besoin en récupérant ces objets dans le conteneur Spring à l'aide de la méthode getBean (String beanName).


3

L'injection de dépendances est au cœur du concept lié à Spring Framework.Tout en créant la structure de n'importe quel projet spring peut jouer un rôle vital, et ici l'injection de dépendances vient en pichet.

En fait, supposons qu'en Java, vous avez créé deux classes différentes en tant que classe A et classe B, et quelles que soient les fonctions disponibles dans la classe B que vous souhaitez utiliser dans la classe A, à ce moment-là, l'injection de dépendance peut être utilisée. où vous pouvez créer un objet d'une classe dans une autre, de la même manière que vous pouvez injecter une classe entière dans une autre classe pour la rendre accessible. de cette façon, la dépendance peut être surmontée.

L'INJECTION PAR DÉPENDANCE, C'EST SIMPLEMENT COLLER DEUX CLASSES ET EN MÊME TEMPS LES GARDER SÉPARÉES.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.