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


Plusieurs questions ont déjà été posées avec des questions spécifiques sur injection de dépendance, comme quand l'utiliser et quels cadres sont là pour cela. cependant,

Qu'est-ce que l'injection de dépendance et quand / pourquoi devrait-il ou non être utilisé?


2626
2017-09-25 00:28


origine


Réponses:


Fondamentalement, au lieu d'avoir vos objets créant une dépendance ou demandant à un objet d'usine d'en créer un pour eux, vous passez les dépendances nécessaires à l'objet en externe, et vous en faites le problème de quelqu'un d'autre. Ce "quelqu'un" est soit un objet plus haut dans le graphe de dépendance, soit un injecteur de dépendances (framework) qui construit le graphe de dépendance. Une dépendance telle que je l'utilise ici est tout autre objet auquel l'objet courant doit contenir une référence.

L'un des principaux avantages de l'injection de dépendance est qu'elle peut faciliter le test des lots. Supposons que vous ayez un objet qui dans son constructeur fait quelque chose comme:

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

Cela peut être gênant quand tout ce que vous voulez faire est d'exécuter des tests unitaires sur SomeClass, surtout si myObject est quelque chose qui fait un accès complexe au disque ou au réseau. Alors maintenant, vous cherchez à moquer myObject, mais aussi d'intercepter l'appel d'usine. Difficile. Au lieu de cela, passez l'objet en tant qu'argument au constructeur. Maintenant vous avez déplacé le problème ailleurs, mais les tests peuvent devenir beaucoup plus faciles. Faites juste un myObject factice et transmettez-le. Le constructeur ressemblerait maintenant à ceci:

public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

C'est un style d'injection de dépendance - via le constructeur. Plusieurs mécanismes sont possibles.

  • Comme indiqué dans les commentaires, une alternative courante consiste à définir un constructeur de do-nothing, et d'avoir les dépendances injectées via des setters de propriétés (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 qu'elles souhaitent injecter.

Lorsque vous n'utilisez pas l'injection de dépendance (par exemple, dans les classes qui fonctionnent trop dans leurs constructeurs, etc.), il devient plus difficile d'isoler les composants dans les tests unitaires.

Retour en 2013 quand 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 vous n'avez pas toujours besoin de la flexibilité supplémentaire dans votre conception d'exécution (par exemple, pour localisateur de service ou modèles similaires), mais vous devez souvent être capable d'isoler vos classes pendant les tests.


1633
2017-09-25 00:49



La meilleure définition que j'ai trouvée jusqu'ici est un par James Shore:

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

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

L'injection de dépendances fournit essentiellement les objets dont un objet a besoin (ses dépendances) au lieu de l'avoir lui-même construit. C'est une technique très utile pour tester, car elle permet de se moquer des dépendances.

Les dépendances peuvent être injectées dans des objets par de nombreux moyens (tels que l'injection du constructeur ou l'injection du setter). On peut même utiliser des frameworks d'injection de dépendance spécialisés (par exemple Spring) pour le faire, mais ils ne sont certainement pas requis. Vous n'avez pas besoin de ces frameworks pour avoir une injection de dépendance. L'instanciation et le passage d'objets (dépendances) explicitement est une aussi bonne injection qu'une injection par framework.


2058
2017-09-26 16:50



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 de l'obtention de ses propres références aux objets dépendants (dépendances) avec lesquels il collabore. Cela conduit à des classes fortement couplées et à du code difficile à tester.

Par exemple, considérez un Car objet.

UNE Car dépend des roues, du moteur, du carburant, de la batterie, etc. pour fonctionner. Traditionnellement, nous définissons la marque de ces objets dépendants avec la définition de la Car objet.

Sans injection de dépendance (DI):

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

  //The rest
}

Ici le Car objet 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 la première NepaliRubberWheel() crevaisons? Nous devons recréer l'objet Car avec sa nouvelle dépendance ChineseRubberWheel()mais seulement le Car le fabricant peut le faire.

Alors qu'est-ce que le Dependency Injection faisons-nous pour ...?

Lors de l'utilisation de l'injection de dépendance, les objets reçoivent leurs dépendances au moment de l'exécution plutôt que de l'heure de compilation (temps de fabrication de la voiture). Alors que nous pouvons maintenant changer le Wheel quand nous voulons. Ici le dependency (wheel) peut être injecté dans Car lors de l'exécution.

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

Nous voilà injection la dépendances (Roue et batterie) à 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;
  }
}

La source: Comprendre l'injection de dépendance


507
2018-05-22 04:01



L'injection de dépendances est une pratique où les objets sont conçus de manière à recevoir des instances des objets provenant d'autres parties du 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érons 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::addManager et PersonService::removeManager aurait besoin d'une instance de GroupMembershipService pour faire son travail. Sans Dependency Injection, la manière traditionnelle de faire cela serait d'instancier un nouveau GroupMembershipService dans le constructeur de PersonService et utilisez cet attribut d'instance dans les deux fonctions. Cependant, si le constructeur de GroupMembershipService a plusieurs choses dont il a besoin, ou pire encore, il y a quelques "setters" d'initialisation qui doivent être appelés sur le GroupMembershipService, le code se développe plutôt rapidement, et le PersonServicemaintenant ne dépend pas seulement de la GroupMembershipService mais aussi tout le reste GroupMembershipService dépend de. En outre, le lien avec GroupMembershipService est codé en dur dans le PersonService ce qui signifie que vous ne pouvez pas "simuler" un GroupMembershipService à des fins de test, ou pour utiliser un modèle de stratégie dans différentes parties de votre application.

Avec Dependency Injection, au lieu d'instancier le GroupMembershipService dans votre PersonService, vous passeriez soit à la PersonService constructeur, ou bien ajoutez une propriété (getter et setter) pour en définir une instance locale. Cela signifie que votre PersonService n'a plus à se soucier de la façon de créer un GroupMembershipService, il accepte simplement ceux qui lui sont donnés, et travaille avec eux. Cela signifie également que tout ce qui est une sous-classe de GroupMembershipServiceou implémente le GroupMembershipService l'interface peut être "injectée" dans le PersonService, et le PersonService n'a pas besoin de connaître le changement.


233
2017-09-25 06:49



La réponse acceptée est bonne, mais je voudrais ajouter que DI est très similaire à 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 le déplacez rapidement de l'intérieur du code vers un fichier de configuration et transmettez une variable contenant cette valeur à l'endroit où cela est nécessaire. La raison de faire cela est que ces constantes changent habituellement 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 à partir du code de classe est similaire - les objets changent plus fréquemment que le code qui les utilise. Un cas important où un tel changement est nécessaire est les tests.


142
2018-01-06 18:33



Imaginons que vous vouliez aller pêcher:

  • Sans injection de dépendance, vous devez prendre soin 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 met 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 prend soin de toute la préparation et met à votre disposition l'équipement requis. Vous recevrez ("soyez injecté") le bateau, la canne à pêche et l'appât - tous prêts à l'emploi.


96
2017-10-22 04:47



Ce est l'explication la plus simple à propos de Injection de dépendance et Conteneur d'injection de dépendance Que j'ai jamais vu:

Sans injection de dépendance

  • L'application nécessite 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, …), alors:
      • Bar crée Bim
      • 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
  • Application crée Bar et lui donne Bim
  • Application crée Foo et lui donne Bar
  • L'application appelle Foo
    • Foo appelle Bar
      • Bar fait quelque chose

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

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

Injection de dépendance et Conteneurs d'injection de dépendance sont des choses différentes:

  • Dependency Injection est une méthode pour écrire du meilleur code
  • un conteneur DI est un outil pour aider à injecter des dépendances

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


80
2018-05-05 11:53



Essayons un exemple simple avec Voiture et Moteur classes, toute voiture a besoin d'un moteur pour aller n'importe où, au moins pour l'instant. Donc, ci-dessous, comment le code se penchera 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 alors nous aurons besoin de réécrire la classe Car. Et plus l'application est importante, plus nous aurons de problèmes et de maux de tête à ajouter 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 des dépendances (DIP) de SOLID. DIP suggère que nous devrions dépendre d'abstractions, pas de classes concrètes. Donc, pour satisfaire cela, nous introduisons IEngine interface et réécrire 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 réelle de moteur concret comme GasEngine ou ElectricityEngine. C'est là que Injection de dépendance entre.

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

Ici, nous injectons (passons) notre dépendance (instance du moteur) 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 Injection de dépendance les classes sont plus faiblement couplées, car elles n'ont pas de dépendances codées en dur. Ceci 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 interfaces) qui leur sont fournis lors de la construction de la classe.

Donc à la fin Injection de dépendance est juste 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 cette classe a besoin   Pour effectuer ses actions, les dépendances sont fournies à la classe   (le plus souvent) via l'injection du constructeur.

De plus, lorsque nous avons de nombreuses dépendances, il est très utile d'utiliser des conteneurs Inversion de Contrôle (IoC) qui permettent de déterminer quelles interfaces doivent être mappées aux implémentations concrètes pour toutes nos dépendances et nous pouvons les résoudre pour nous quand elles construisent notre objet. Par exemple, nous pourrions spécifier dans le mappage du conteneur IoC que IEngine la dépendance devrait être mappé à la Moteur à gaz classe et quand nous demandons au conteneur IoC pour une instance de notre Voiture classe, il construira automatiquement notre Voiture classe avec un Moteur à gaz dépendance passée.

METTRE À JOUR: Regardé cours sur EF Core de Julie Lerman récemment et a également aimé sa courte définition de DI.

L'injection de dépendance est un modèle permettant à votre application d'injecter   objets à la volée pour les classes qui en ont besoin, sans forcer les   classes pour être responsable de ces objets. Cela permet à votre code d'être   plus lâchement couplé, et Entity Framework Core se branche à ce même   système de services.


77
2017-07-06 09:42