web-dev-qa-db-fra.com

Différence entre l'injection de dépendance et l'inversion de dépendance

Il existe deux modèles de conception, à savoir l'injection de dépendance et l'inversion de dépendance, les articles sont là dans le filet pour essayer d'expliquer la différence. Mais la nécessité de l'expliquer en termes plus simples est toujours là. Quelqu'un là-bas à venir?

J'ai besoin de le comprendre en PHP.

18
Istiaque Ahmed

(Remarque: cette réponse est indépendante du langage, bien que la question mentionne spécifiquement PHP, mais étant peu familier avec PHP, je n'ai fourni aucun PHP).

Injection vs inversion

  • Dépendance Injection est une technique Inversion de contrôle pour fournir objets ('dépendances') à un par le biais du Dependency Injection Design Pattern . Passer généralement des dépendances via l'un des éléments suivants:

    • Un constructeur
    • Une propriété ou un domaine public
    • Un setter public
  • La dépendance Inversion Principe (DIP) est une conception logicielle directive qui se résume à deux recommandations sur découplage d'une classe de ses dépendances concrètes =:

    1. 'Les modules de haut niveau ne doivent pas dépendre de modules de bas niveau. Les deux devraient dépendre des abstractions. '
    2. 'Les abstractions ne doivent pas dépendre des détails. Les détails devraient dépendre des abstractions. '

Injection de dépendance

Premièrement, L'injection de dépendances n'est pas la même chose que l'inversion de contrôle (IoC). Plus précisément, l'IoC est un parapluie de différentes solutions, y compris l'injection de dépendance, le localisateur de service, le modèle d'usine, le modèle de stratégie et autres.

Une classe conçue avec Dependency Injection à l'esprit peut ressembler à ceci:

// Dependency Injection Example...

class Foo {
    // Constructor uses DI to obtain the Meow and Woof dependencies
    constructor(fred: Meow, barney: Woof) {
        this.fred = fred;
        this.barney = barney;
    }
}

Dans cet exemple, Meow et Woof sont tous deux des dépendances injectées via le constructeur Foo.

D'un autre côté, une classe Foo conçue sans L'injection de dépendances pourrait simplement créer les instances Meow et Woof elle-même, ou peut-être utiliser une sorte de localisateur de service/usine:

// Example without Dependency Injection...

class Foo {
    constructor() {
        // a 'Meow' instance is created within the Foo constructor
        this.fred = new Meow();

        // a service locator gets a 'WoofFactory' which in-turn
        // is responsible for creating a 'Woof' instance.
        // This demonstrates IoC but not Dependency Injection.
        var factory = TheServiceLocator.GetWoofFactory();
        this.barney = factory.CreateWoof();
    }
}

Ainsi, l'injection de dépendances signifie simplement qu'une classe a différé responsabilité d'obtenir ou de fournir ses propres dépendances; au lieu de cela, cette responsabilité incombe à tout ce qui veut créer une instance. (Qui est généralement un conteneur IoC)


Inversion de dépendance

L'inversion de dépendance consiste généralement à découpler des classes concrètes en empêchant ces classes d'avoir une référence directe les unes aux autres.

Remarque: l'inversion de dépendance est souvent plus explicite dans les langages de programmation à typage statique tels que C # ou Java, car ces langages imposent une vérification de type stricte sur les noms de variables. D'un autre côté, Dependency Inversion est déjà disponible passivement dans des langages dynamiques tels que Python ou JavaScript car les variables dans ces langages n'ont pas de restrictions de type particulières.

Envisagez un scénario dans un langage de type statique où une classe nécessite la capacité de lire un enregistrement de la base de données de l'application:

class Foo {
    reader: SqlRecordReader;

    constructor(sqlReader: SqlRecordReader) {
        this.reader = sqlReader;
    }

    doSomething() {
        var records = this.reader.readAll();
        // etc.
    }
}

Dans l'exemple ci-dessus, la classe Foo a une forte dépendance sur SqlRecordReader, mais la seule chose qui la tient vraiment à cœur est qu'il existe une méthode appelée readAll() qui retourne certains enregistrements .

Considérez la situation où les requêtes de base de données SQL sont séparées en microservices distincts; la classe Foo devrait plutôt lire les enregistrements d'un service de suppression. Ou bien, une situation où Foo tests unitaires doit lire des données à partir d'un magasin en mémoire ou d'un fichier plat.

Si, comme son nom l'indique, SqlRecordReader contient une base de données et une logique SQL, tout passage aux microservices aurait besoin de la classe Foo pour changer.

Les directives d'inversion de dépendance suggèrent que SqlRecordReader devrait être remplacé par une abstraction qui ne fournit que la méthode readAll(). c'est à dire:

interface IRecordReader {
    Records[] getAll();
}

class Foo {
    reader: IRecordReader;

    constructor(reader: IRecordReader) {
        this.reader = reader;
    }
}

Selon le DIP Le IRecordReader est une abstraction, et forçant Foo à dépendre de IRecordReader au lieu de SqlRecordReader satisfait Lignes directrices DIP.


Pourquoi les directives DIP sont utiles

Le mot-clé est guide - l'inversion de dépendance ajoute une indirection à la conception de votre programme. L'inconvénient évident de l'ajout de tout type d'indirection est que la complexité (c'est-à-dire la "charge" cognitive requise pour qu'un être humain comprenne ce qui se passe) augmente.

Dans de nombreux cas, l'indirection peut faciliter la maintenance du code (correction de bugs, ajout d'améliorations) cependant:

Dans ce dernier exemple, Foo pourrait recevoir un SqlRecordReader, ou peut-être un SoapRecordReader, ou peut-être un FileRecordReader, ou peut-être même pour les tests unitaires d'un MockRecordReader - le fait est qu'il ne sait rien des différentes implémentations possibles de IRecordReader - à condition bien sûr que ces implémentations soient à la hauteur de Principe de substitution de Liskov .

De plus, cela évite le scénario potentiellement sale où un développeur qui est pressé de faire fonctionner quelque chose pourrait envisager d'essayer de "tromper" le principe de Liskov en héritant du SoapRecordReader ou FileRecordReader d'une classe de base SqlRecordReader.

Pire encore, un développeur inexpérimenté pourrait même changer le SqlRecordReader lui-même afin que la classe ait une logique non seulement pour SQL mais aussi pour SOAP endpoints, le système de fichiers et tout ce qui pourrait être (Ce genre de chose se produit trop souvent dans le monde réel - en particulier dans le code mal entretenu, et est presque toujours un Code Smell .)

33
Ben Cottrell

Voir cet article ici

L'auteur différencie ces deux en termes simples. Injection de dépendance == "Donnez-moi" et Inversion de dépendance == "Quelqu'un s'occupe de cela pour moi, d'une manière ou d'une autre." . Dans le principe d'inversion de dépendance, le module de haut niveau est le propriétaire de l'abstraction. Ainsi le détail (implémentation de l'abstraction) dépend de l'abstraction et dépend donc du module de haut niveau. Dépendance inversée! .. L'injection de dépendance est différente. L'abstraction peut ne pas être conservée par le module de haut niveau. Ainsi, une abstraction donnée à l'objet de niveau supérieur peut ne pas être limitée aux besoins du module de haut niveau.

Inversion de dépendance:

Vous avez un module de niveau supérieur X et une abstraction Y qui est définie par X. Z implémente Y et est donné à X. Ainsi Z dépend de X (via l'abstraction Y définie par X).

Injection de dépendance:

Vous avez un module X de niveau supérieur qui a besoin des fonctionnalités A et B. Y est une abstraction qui contient les fonctionnalités A, B et C. Z implémente Y. Puisque Z implémente Y et a donc les fonctionnalités A et B, Z est donné à X. Maintenant, X dépend de Y.

2
AJA

L'injection de dépendance est un moyen d'obtenir une inversion de contrôle (que je suppose que vous faites référence à l'inversion de dépendance), donc les deux ne sont pas vraiment en concurrence autant que DI est une spécialisation de l'IoC. D'autres moyens couramment utilisés pour atteindre l'IoC incluent l'utilisation d'usines ou le modèle Service Locator.

1
Kyle Burns