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.
(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).
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:
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 =:
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)
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.
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 .)
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.
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.