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




injection de dépendance symfony (20)

Plusieurs questions ont déjà été posées concernant des questions spécifiques sur l' injection de dépendance , telles que le moment d'utilisation et les cadres de travail. cependant,

Qu'est-ce que l'injection de dépendance et quand / pourquoi devrait-on ou non l'utiliser?


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. Dire que l'objet A dépend de l'objet B, il s'agit donc de découpler ces objets les uns des autres. Nous n'avons pas besoin de coder en dur l'objet à l'aide d'un nouveau mot clé, mais plutôt de partager des dépendances avec des objets lors 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 à l'aide d'un nouveau mot clé, mais plutôt de définir la dépendance du bean dans le fichier de configuration. Le conteneur à ressort sera responsable de la connexion de tous.

Inversion de contrôle (IOC)

La COI est un concept général qui peut s’exprimer de différentes manières. L’injection de dépendance est un exemple concret de la COI.

Deux types d'injection de dépendance:

  1. Injection de constructeur
  2. Injection de poseur

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

Une DI basée sur un constructeur est accomplie lorsque le conteneur appelle un constructeur de classe avec un certain nombre d'arguments, chacun représentant une dépendance vis-à-vis 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 le poseur:

La DI basée sur le sélecteur est accomplie par les méthodes de définition d'appel du conteneur sur vos beans après avoir appelé un constructeur sans argument ou une méthode de fabrique 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: En règle générale, il est conseillé 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 l'annotation @Required sur un setter, elle peut être utilisée pour créer des setters en tant que dépendances requises.


Essayons un exemple simple avec les classes de voitures et de moteurs : toute voiture a besoin d’un moteur pour aller n’importe où, du moins pour le moment. Donc, en dessous, à 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 à ElectricityEngine, nous devrons réécrire la classe Car. Et plus l'application est volumineuse, plus nous aurons à ajouter de nouveaux problèmes et à utiliser un nouveau type de moteur.

En d'autres termes, avec cette approche, notre classe de voiture de haut niveau dépend de la classe GasEngine de niveau inférieur, qui enfreint le principe d'inversion de dépendance (DIP) de SOLID. DIP suggère de 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 suit:

    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, et non d'une implémentation spécifique du moteur. Maintenant, la seule astuce consiste à créer une instance de la voiture et à lui attribuer une classe de moteur concrète telle que GasEngine ou ElectricityEngine. C’est là que l’ injection de dépendance entre en jeu.

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

Ici, nous injectons (passons) notre dépendance (instance de moteur) au constructeur de voitures. 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 plus faiblement couplées, car elles n'ont pas de dépendances codées en dur. Ceci est conforme au principe d'inversion de dépendance 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, au final, l’ injection de dépendance n’est qu’une technique permettant de 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 exécuter ses actions, les dépendances sont fournies à la classe (le plus souvent) via une injection de constructeur.

De même, lorsque nous avons de nombreuses dépendances, il est très recommandé d’utiliser des conteneurs Inversion of Control (IoC) pour indiquer quelles interfaces doivent être mappées vers quelles implémentations concrètes pour toutes nos dépendances et pouvoir les résoudre pour nous lorsqu’elles construisent. 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é en.

UPDATE: J'ai récemment visionné un cours sur EF Core de Julie Lerman et aussi apprécié sa courte définition de DI.

L'injection de dépendance 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 souple, et Entity Framework Core se connecte à ce même système de services.


Imaginons que vous vouliez aller pêcher:

  • 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 un appât, etc. C'est possible, bien sûr, mais cela vous impose beaucoup de responsabilités. En termes logiciels, cela signifie que vous devez effectuer une recherche pour toutes ces choses.

  • Avec l'injection de dépendance, une autre personne se charge de toute la préparation et met à votre disposition l'équipement requis. Vous recevrez ("serez injecté") le bateau, la canne à pêche et l’appât - prêts à l’emploi.


L'injection de dépendance est une pratique dans laquelle les objets sont conçus de manière à recevoir leurs instances 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é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, l'implémentation de PersonService::addManager et de PersonService::removeManager aurait besoin d'une instance de GroupMembershipService pour effectuer son travail. Sans l'injection de dépendance, la méthode traditionnelle consiste à instancier un nouveau GroupMembershipService dans le constructeur de PersonService et à utiliser cet attribut d'instance dans les deux fonctions. Toutefois, si le constructeur de GroupMembershipService comporte plusieurs éléments, voire pire, certains "ajusteurs" d'initialisation doivent être appelés sur GroupMembershipService , le code croît assez rapidement et le PersonService dépend désormais non seulement de GroupMembershipService mais ainsi que tout ce dont GroupMembershipService dépend. De plus, le lien avec GroupMembershipService est codé en dur dans PersonService ce qui signifie que vous ne pouvez pas "masquer" 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 GroupMembershipService dans votre PersonService , vous le transmettiez au constructeur PersonService ou ajoutiez une propriété (getter et setter) pour en définir une instance locale. Cela signifie que votre PersonService n'a plus à s'inquiéter de la façon de créer un GroupMembershipService , il accepte uniquement ceux qui lui sont fournis et fonctionne avec eux. Cela signifie également que tout ce qui est une sous-classe de GroupMembershipService ou implémente l'interface GroupMembershipService peut être "injecté" dans le PersonService , et le PersonService n'a pas besoin de connaître le changement.


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 consiste à donner à un objet ses variables d'instance. [...]

Il existe également un article de Martin Fowler qui pourrait s’avérer utile.

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

Les dépendances peuvent être injectées dans des objets par de nombreux moyens (tels que l'injection de constructeur ou l'injection de setter). On peut même utiliser des infrastructures d'injection de dépendances spécialisées (Spring, par exemple), mais elles ne sont certainement pas nécessaires. Vous n'avez pas besoin que ces frameworks aient une injection de dépendance. Instancier et passer des objets (dépendances) explicitement est une injection aussi bonne que l’injection par framework.


La réponse acceptée est bonne - mais j’aimerais ajouter à cela que DI est très semblable à la méthode classique qui consiste à éviter les 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, puis transmettez une variable contenant cette valeur à l'emplacement où elle est requise. La raison en est que ces constantes changent généralement plus souvent 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 présentes au lieu de littéraux constants sont des objets entiers - mais la raison pour déplacer le code les créant en dehors du code de classe est similaire: les objets changent plus souvent que le code qui les utilise. Les tests constituent un cas important dans lequel un tel changement est nécessaire.


L'injection de dépendance désigne un moyen (en réalité n'importe quel moyen ) permettant à une partie du code (par exemple, une classe) d'accéder aux dépendances ils peuvent changer ou être annulés librement, ou même être chargés à un autre moment, au besoin)

(et ps, oui, c’est devenu un nom trop surfacé à 25 $ pour un concept plutôt simple) , mes .25centimes


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

Toute application est composée de nombreux objets qui collaborent les uns avec les autres 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 à un code difficile à tester.

Par exemple, considérons un objet Car .

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 l'objet Car .

Sans injection de dépendance (DI):

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

  //The rest
}

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

Que faire si nous voulons changer le type de son objet dépendant - par exemple Wheel - après la NepaliRubberWheel() initiale de NepaliRubberWheel() ? Nous devons recréer l'objet Car avec sa nouvelle dépendance, par exemple ChineseRubberWheel() , mais seul le constructeur Car peut le faire.

Alors qu'est-ce que l' Dependency Injection nous fait pour ...?

Lors de l'utilisation de l'injection de dépendance, les dépendances des objets sont définies au moment de l'exécution plutôt que lors de la compilation (temps de fabrication de la voiture) . Pour que nous puissions maintenant changer la Wheel quand nous le voulons. Ici, la dependency ( wheel ) peut être injectée dans Car au moment de l'exécution.

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

Ici, nous injectons les dépendances (Roue et Batterie) au moment 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


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

Comme d'autres l'ont déjà 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 consommateur) (au sens UML ). Ces instances sont plutôt transmises à notre classe de consommateurs, généralement sous la forme de paramètres de constructeur ou via des paramètres de propriété (la gestion de l'instanciation et de la transmission de l'objet de dépendance à la classe de consommateur est généralement effectuée par un conteneur Inversion of Control (IoC) , 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, DI est l’une des applications possibles du principe d’inversion de dépendance (DIP, Dependency Inversion Principle) . Le DIP est le D du mantra SOLID - les autres implémentations de DIP incluent les modèles Service Locator et Plugin.

L’objectif du DIP est de découpler les dépendances concrètes entre les classes et, au contraire, de desserrer le couplage au moyen d’une abstraction pouvant être réalisée via une interface , une abstract class ou pure virtual class , en fonction du langage et de l’approche utilisés .

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

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

Attendu 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 dépendance à Foo a été supprimé:

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

Pourquoi utiliser DIP (et DI)?

Le découplage des dépendances entre les classes de cette manière permet une substitution facile de ces classes de dépendances à 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). En outre, comme d'autres l'ont mentionné, la raison la plus courante de découpler des classes via le DIP est de permettre à une classe consommatrice d'être testée de manière isolée, ces mêmes dépendances pouvant désormais être modifiées et simulées.

L'une des conséquences de DI est que la gestion de la durée de vie des instances d'objet de dépendance n'est plus contrôlée par une classe consommatrice, car l'objet de dépendance est maintenant transmis à 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 la création des instances de la classe de dépendances dans la classe du consommateur. Le consommateur pourra obtenir des instances via une Create en usine selon les besoins et disposer de ces instances une fois l'opération terminée.
  • Ou bien, le contrôle de la durée de vie des instances de dépendance peut être transféré dans 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 indépendamment de ses dépendances,
  • Où l'incertitude de la durée de vie d'une dépendance peut justifier une expérimentation (par exemple, Hey, MyDepClass est thread-safe - que se passe-t-il si nous en faisons un singleton et que nous injectons la même instance à tous les consommateurs?)

Exemple

Voici une implémentation simple de C #. Compte tenu de la classe consommatrice 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 dépendances static sur deux autres classes, System.DateTime et System.Console , qui non seulement limitent les options de sortie de la journalisation (la journalisation sur la console sera inutile si personne ne les surveille), mais pire encore, il est difficile pour tester automatiquement étant donné la dépendance à une horloge système non déterministe.

Nous pouvons cependant appliquer DIP à cette classe, en MyLogger le problème de l'horodatage en tant que dépendance et en couplant MyLogger à une interface simple:

public interface IClock
{
    DateTime Now { get; }
}

Nous pouvons également réduire la dépendance de la Console à une abstraction, telle qu'un TextWriter . L'injection de dépendance est généralement implémentée sous forme d'injection de constructor (transmission d'une abstraction à une dépendance en tant que paramètre du constructeur d'une classe consommatrice) ou d' Setter Injection (transmission de la dépendance via un setter setXyz() 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 permettra aux champs de dépendance internes d'être marqués comme étant en readonly (C #) ou final (Java). Donc, en utilisant l'injection de constructeur sur l'exemple ci-dessus, cela nous laisse avec:

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);
    }
}

(Une Clock concrète doit être fournie, qui pourrait bien sûr revenir à DateTime.Now , et les deux dépendances doivent être fournies par un conteneur IoC via une 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 - l’heure, 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épendance est invariablement associée à un conteneur Inversion of Control (IoC) afin d'injecter (fournir) les instances de dépendance concrètes et de gérer les instances de durée de vie. Au cours du processus de configuration / d’amorçage, les conteneurs IoC 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 demande un IBar , renvoie une instance ConcreteBar " )
  • des stratégies peuvent être configurées 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.
  • En .Net, les conteneurs IoC connaissent des protocoles tels que, IDisposableet assumeront 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 que les conteneurs IoC ont été configurés / initialisés, ils fonctionnent de manière transparente en arrière-plan, ce qui permet au codeur de se concentrer sur le code disponible plutôt que de se soucier des dépendances.

La clé du code convivial pour les DI consiste à éviter le couplage statique de classes et à 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. Pour le développeur, un changement de paradigme est nécessaire pour rompre l'habitude de newgérer directement les dépendances et de faire confiance au conteneur pour les gérer.

Mais les avantages sont nombreux, notamment en ce qui concerne la possibilité de tester en profondeur votre classe d’intérêts.

Remarque : La création / la cartographie / la projection (via new ..()) de DTO POCO / POJO / de sérialisation / de graphiques d'entité / de projections JSON anonymes et autres, à savoir des classes ou des enregistrements "Données uniquement" utilisées ou renvoyées à partir de méthodes, ne sont pas considérées comme des dépendances Sens UML) et non soumis à DI. Utiliser newpour les projeter est très bien.


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

L'injection de dépendance est l'un des modèles de conception qui nous aide à 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 exemples sont magnétophone, VCD, lecteur de CD, etc.

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

L’intention principale d’un magnétophone est d’enregistrer ou de reproduire un son.

Lors de la conception d'un système, il faut une bobine pour enregistrer ou lire du son ou de la musique. Il y a 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 de bobine. si nous optons pour le second, qui consiste à placer un crochet pour bobine, nous obtenons un avantage supplémentaire en jouant de la musique en changeant de bobine. et aussi en réduisant la fonction uniquement au jeu dans la bobine.

De même, l'injection de dépendance consiste à externaliser les dépendances pour se concentrer uniquement sur la fonctionnalité spécifique du composant, de sorte que des composants indépendants puissent être couplés 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 sur la responsabilité.
  • Faire des choses en tant que composants et combiner pour former un grand système avec des capacités élevées
  • Il est utile de 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 d'échec.

De nos jours, ce concept constitue la base de cadres bien connus dans le monde de la programmation. Les Spring Angular, etc. sont les frameworks logiciels bien connus construits sur le dessus de ce concept

L'injection de dépendance est un modèle utilisé pour créer des occurrences d'objets sur lesquelles s'appuient d'autres objets sans savoir au moment de la compilation quelle classe sera utilisée pour fournir cette fonctionnalité ou simplement le mode d'injection de propriétés à un objet est appelé 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 enlève 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 aussi lire

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


L’injection de dépendance est au cœur du concept associé à Spring Framework. Tout en créant la structure d’un projet, le printemps peut jouer un rôle vital. Dans ce cas, l’injection de dépendance est un outil indispensable.

En fait, supposons que vous ayez créé en Java deux classes différentes (classes A et B) et que, quelles que soient les fonctions disponibles en classe B, vous souhaitiez les utiliser en classe A. 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 le rendre accessible. de cette façon, la dépendance peut être surmontée.

L'INJECTION DE DÉPENDANCE COLLE SIMPLEMENT DEUX CLASSES ET, EN MÊME TEMPS, EN LES TENANT SÉPARÉES.


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

Le but de l'injection de dépendance est de découpler le travail de résolution des composants logiciels externes de votre logique métier d'application. Sans l'injection de dépendance, les détails de la manière dont un composant accède aux services requis peuvent être confus avec son code. Cela augmente non seulement le potentiel d'erreurs, ajoute du code et augmente la complexité de la maintenance; il couple plus étroitement les composants, ce qui rend difficile la modification des dépendances lors de la refactorisation ou du test.


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 principe de printemps du CIO 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 un 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 les récupérant à l'aide de la méthode getBean (String beanName).


Exemple, nous avons 2 classes Clientet Service. ClientutiliseraService

public class MyDao {

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

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

}

Sans injection de dépendance

Voie 1)

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)
  {...}

}

Voie 2)

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

Voie 3)

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

1) 2) 3) Utilisation

gcc foo.o bar.o -o bar

Les avantages

  • Simple

Désavantages

  • Dur pour la Clientclasse de test
  • Lorsque nous changeons de Serviceconstructeur, nous devons changer de code dans tous les lieux create Serviceobject

Utiliser l'injection de dépendance

Voie 1) Injection de constructeur

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

En utilisant

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

Voie 2) Injection de poseur

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

En utilisant

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

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 toujours new Service()beaucoup de temps et il n’est pas bon de changer de Serviceconstructeur. Pour éviter cela, nous pouvons utiliser l’injecteur DI comme
1) Manuel simpleInjector

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

En utilisant

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();
    }
}

2) Utiliser la bibliothèque: Pour Android dagger2

Les avantages

  • Faciliter le test
  • Lorsque vous changez le Service, il vous suffit de le changer dans la classe Injector
  • Si vous utilisez use Constructor Injection, lorsque vous regardez 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, nous utilisons parfois function in Clientclass sans utiliser, de Servicesorte que créé Serviceest gaspillé.

Définition d'injection de dépendance

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

Une dépendance est un objet utilisable ( Service)
Une injection est le passage d’une dépendance ( Service) à un objet dépendant ( Client) qui l’utiliserait.


Je proposerais une définition légèrement différente, concise et précise de ce qu'est une dépendance injectable, en se concentrant sur l'objectif principal et non sur les moyens techniques (à partir de here ):

L'injection de dépendance est le processus de création du graphe statique et sans état d'objets de service, où chaque service est paramétré par ses dépendances.

Les objets que nous créons dans nos applications (que nous utilisions Java, C # ou un autre langage orienté objet) appartiennent généralement à l'une des deux catégories suivantes: «objets de service» stateless, globaux et globaux «Objets de données».

Le graphique de module - le graphique des objets de service - est généralement créé au démarrage de l'application. Cela peut être effectué à l'aide d'un conteneur, tel que Spring, mais également manuellement, en passant des paramètres aux constructeurs d'objet. Les deux méthodes ont leurs avantages et leurs inconvénients, mais un cadre n'est certainement pas nécessaire pour utiliser DI dans votre application.

Une exigence est que les services doivent être paramétrés par leurs dépendances. Ce que cela signifie exactement dépend de la langue et de l'approche adoptées dans un système donné. Généralement, cela prend la forme de paramètres de constructeur, mais l’utilisation de setters est également une option. Cela signifie également que les dépendances d'un service sont masquées (lors de l'appel d'une méthode de service) des utilisateurs du service.

Quand utiliser? Je dirais que chaque fois que l'application est suffisamment volumineuse pour que la logique soit encapsulée dans des modules séparés, un graphe de dépendance entre les modules procure un gain de lisibilité et d'exploration du code.


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:

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

Dépendance:

public class Client {
    Service service;

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

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

Notez comment l' DataSourceImplinstanciation est déplacée dans un constructeur. Le constructeur prend quatre paramètres qui sont les quatre valeurs nécessaires au fichier DataSourceImpl. Bien que la MyDaoclasse dépende toujours de ces quatre valeurs, elle ne satisfait plus ces dépendances elle-même. Ils sont fournis par n'importe quelle classe créant une MyDaoinstance.


L'injection de dépendance (ID) fait partie de la pratique du principe d'inversion de dépendance (DIP), également appelée Inversion of Control (IoC). Fondamentalement, vous devez utiliser DIP car vous voulez rendre votre code plus modulaire et testable à l'unité, au lieu d'un seul système monolithique. Vous commencez donc à identifier les parties du code qui peuvent être séparées de la classe et extraites de manière abstraite. Maintenant, l'implémentation de l'abstraction doit être injectée de l'extérieur de la classe. Normalement, cela peut être fait via le constructeur. Vous créez donc un constructeur qui accepte l’abstraction en tant que paramètre, il s’appelle injection de dépendance (via constructeur). Pour plus d'explications sur les conteneurs DIP, DI et IoC, vous pouvez lire Here


L'injection de dépendance est une solution possible à ce que l'on pourrait généralement appeler l'exigence d '"obfuscation de dépendance". L'obscurcissement de la dépendance est une méthode permettant de supprimer le caractère "évident" du processus consistant à créer une dépendance à une classe qui l'exige et, partant, de masquer, d'une manière ou d'une autre, la fourniture de ladite dépendance à ladite classe. Ce n'est pas forcément une mauvaise chose. En fait, en masquant la manière dont une dépendance est fournie à une classe, un élément extérieur à 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 modification. à 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 «fictive»).

Malheureusement, le mauvais côté est que certaines personnes ont supposé que vous aviez besoin d'un framework spécialisé pour faire de l'obscurcissement des dépendances et que vous êtes en quelque sorte un programmeur "mineur" si vous choisissez de ne pas utiliser un framework particulier. Un autre mythe extrêmement troublant, selon de nombreuses personnes, est que l’injection de dépendance est le seul moyen d’obtenir l’obscurcissement de la dépendance. Ceci est manifestement, 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 depuis des années l'exigence de dépendance liée à la dépendance et de nombreuses solutions alternatives ont été développées avant et après la conception de l'injection de dépendance. Il existe des modèles d’usine, mais de nombreuses options utilisant ThreadLocal ne nécessitent pas d’injection dans un cas particulier - la dépendance est effectivement injectée dans le thread, ce qui présente l’avantage de rendre l’objet disponible (à l’aide de méthodes de getstatique statiques) à toutclasse qui en a besoin sans avoir à ajouter d’annotations aux classes qui en ont besoin et à mettre en place une "colle" XML complexe pour que cela se produise. Lorsque vos dépendances sont requises pour la persistance (JPA / JDO ou autre), cela vous permet d’obtenir une «persistance transaparente» beaucoup plus facile et avec des classes de modèle de domaine et de modèle d’entreprise composées uniquement de POJO (c’est-à-dire sans annotations spécifiques à un cadre / bloquées).


Le point essentiel de l’Injection de dépendance est de garder le code source de l’application propre et stable :

  • nettoyage du code d'initialisation de la dépendance
  • stable indépendamment de la dépendance utilisée

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

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

Exemple: DI avec script shell

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

Considérons 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 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 "[email protected]"
}

Au lieu d' sourceutiliser le script d'implémentation directement dans le fichier 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 une dépendance injectée qui implémente en archive_filesutilisant tarou xz.

Exemple: retirer DI

Si le dependent.shscript utilisait directement les dépendances, l'approche s'appellerait recherche de dépendance (ce qui est opposé à 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 l'initialisation lui-même.

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

Derniers mots

DI n’est pas aussi largement mis en avant 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 d'une version 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épendance n'aide pas, car le nombre de paramètres de configuration peut changer par dépendance (par exemple, un nouveau type d'authentification) ainsi que le nombre de types de dépendances pris en charge (par exemple, le nouveau type de base de données).


Les réponses les plus courantes ne sont d'aucune aide, car elles définissent l'injection de dépendance d'une manière inutile. Admettons 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 "injection de dépendance" quand nous disons

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

Nous appelons simplement cela passer des 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 certaine logique est retirée de l'appelant. Ce n'est pas le cas lorsque l'appelant transmet des paramètres. Par conséquent, si cela était DI, DI n'impliquerait pas d'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épendance. "L'appelant" est la personne qui tape "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 fait un

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(...));
    }
}

avant de faire un

Service service = Injector.provideService();

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

L'objectif principal du niveau intermédiaire n'est pas simplement de transmettre les dépendances au constructeur, mais de répertorier toutes les dépendances à un seul endroit et de les masquer au codeur (et non au codeur de les fournir).

Habituellement, le niveau intermédiaire fournit des fabriques pour les objets construits, qui doivent fournir un rôle que chaque type d'objet demandé doit remplir. En effet, en ayant un niveau intermédiaire qui cache les détails de la construction, vous avez déjà subi la pénalité d'abstraction imposée par les usines. Vous pouvez donc aussi utiliser des usines.





terminology