Qu’est-ce que l’injection de dépendances dans la programmation? –
L’injection de dépendances est une technique de génie logiciel dans laquelle les objets sont des instances passées des autres objets dont ils dépendent. Au lieu d’atteindre eux-mêmes les dépendances extérieures, les objets doivent recevoir directement tout ce dont ils ont besoin pour fonctionner.
Sommaire
Un exemple de base
Le terme «injection de dépendances» et sa définition peuvent paraître complexes. En pratique, c’est un concept simple. Regardons deux approches pour créer un objet de base. Nous utilisons PHP ici mais les concepts s’appliquent à toutes les bases de code orientées objet.
Sans injection de dépendance
class UserCreator { protected Mailer $Mailer; public function __construct() { $this -> Mailer = new Mailer(); } public function create(string username, string $email) : void { $this -> Mailer -> send($email, "Your account", "Welcome."); } }
Avec injection de dépendance
class UserCreator { protected Mailer $Mailer; public function __construct(Mailer $mailer) { $this -> Mailer = $Mailer; } public function create(string $username, string $email) : void { $this -> Mailer -> send($email, "Your account", "Welcome."); } }
La différence est subtile mais significative. Les deux classes ont un dépendance sur un Mailer
exemple. La première classe construit la sienne Mailer
, alors que le second fonctionne avec un Mailer
fournis par le monde extérieur. Le Mailer
la dépendance a été injecté dans la classe.
Avantages de l’injection de dépendance
Le principal avantage de l’injection de dépendances est le découplage des classes et de leurs dépendances qu’elle fournit. L’injection de dépendance est une forme de inversion de contrôle – au lieu de classes contrôlant leurs propres dépendances, elles travaillent avec des instances fournies par leur environnement extérieur.
Plus concrètement, l’injection de vos dépendances simplifie la modification de ces dépendances à l’avenir. Dans une vraie base de code, Mailer
dans notre exemple ci-dessus serait probablement une interface avec des implémentations telles que SendmailMailer
, SendGridMailer
et FakeMailer
.
En prenant le premier exemple, demander aux classes de construire directement l’un des Mailer
les instances entraînent un travail supplémentaire si vous devez ensuite les remplacer Mailer
la mise en oeuvre. Avec l’injection de dépendances, vous pouvez taper Mailer
interface pour accepter toute implémentation compatible. L’implémentation à utiliser est déterminée par le monde extérieur.
La plupart du temps, vous utiliserez l’injection de dépendances en conjonction avec un conteneur d’injection de dépendance. Les conteneurs s’intègrent généralement au cadre d’application que vous utilisez. Ils résolvent et injectent automatiquement les dépendances de classe.
Demander un conteneur pour un UserCreator
construirait d’abord un Mailer
exemple. Cela serait transmis au UserCreator
via son paramètre constructeur. Vous «câblez» le conteneur pour définir l’implémentation à utiliser lorsqu’une interface est typée. Sous ce modèle, changer l’actif Mailer
sur toute la base de code, il suffit de recâbler le conteneur. Cela peut être une ligne de code dans la configuration du conteneur.
Impacts sur les tests
L’injection de dépendances simplifie la simulation de vos dépendances lors des tests. Étant donné que les dépendances proviennent de l’extérieur de la classe, vous pouvez fournir une fausse implémentation dans vos tests unitaires:
class FakeMailer implements Mailer { public function send(string $to, string $subject, string $body) : void { return; } }
Nous n’avons pas besoin d’envoyer l’e-mail de bienvenue lors du test de notre UserCreator
. Néanmoins, ce serait inévitable dans le premier exemple, où UserCreator
construit toujours son propre live Mailer
. Avec l’injection de dépendances, nous pouvons fournir un Mailer
qui satisfait le contrat de l’interface mais élimine les effets secondaires.
Couplage lâche
L’injection de dépendances ne consiste pas à éliminer complètement les dépendances – elles seront toujours là, mais elles devraient être faiblement couplées. Pensez à une dépendance comme à quelque chose qui est attaché pendant une période de temps, pas à un appareil qui est collé en permanence.
Le couplage serré comme indiqué dans le premier exemple rend le code inflexible difficile à déplacer. Les dépendances deviennent opaques pour les observateurs extérieurs tels que les testeurs. Les interfaces de type hinting passées dans votre classe gardent vos dépendances aussi faiblement couplées que possible.
Principe de responsabilité unique
Un dernier avantage de l’injection de dépendances est sa capacité à vous aider à adhérer au principe de responsabilité unique. Cela indique que chaque classe doit avoir la responsabilité d’une seule unité de fonctionnalité autonome dans votre base de code.
Sans injection, les classes fournissent non seulement des fonctionnalités, mais construisent également leurs propres dépendances. Cela nécessite que chaque classe possède une connaissance détaillée des exigences des autres sous-systèmes. Avec l’injection, la connaissance du monde extérieur par la classe est limitée aux contrats fournis par les interfaces dont elle dépend.
Conclusion
L’injection de dépendances facilite la gestion des bases de code orientées objet. Les classes qui construisent leurs propres dépendances sont généralement une odeur de code. Ils finissent par être étroitement liés à leur environnement et sont difficiles à tester.
Sortir les dépendances des classes qui les utilisent inverse le contrôle et crée une séparation plus forte des préoccupations. Vous pouvez considérer cela comme la suppression des constantes codées en dur: vous n’écririez jamais un nom de schéma de base de données dans votre source, car il devrait résider dans un fichier de configuration.
L’implémentation de Mailer
est également un détail de configuration. UserCreator
ne devrait pas se préoccuper de la mise en place d’un système de messagerie. Il y a de fortes chances que vous souhaitiez réutiliser le même système ailleurs dans votre base de code. La configuration change plus fréquemment que le code; vous voudrez probablement changer la manière dont le courrier est envoyé bien avant de réviser l’exigence commerciale selon laquelle un courrier électronique est envoyé lorsqu’un utilisateur s’inscrit.
En résumé, l’injection de dépendances encourage vos composants à demander la fonctionnalité dont ils ont besoin, au lieu de la saisir au coup par coup au cas par cas. Le câblage d’instance au niveau de l’application via un conteneur expose les dépendances pour ce qu’elles sont: des détails de configuration susceptibles de changer. Codez vos classes pour accepter des abstractions de l’environnement, au lieu d’implémentations concrètes construites en interne.