L'injection de dépendances

Produire du code testable et découplé.

Le but de ce tutoriel est d'expliquer l'injection de dépendances (Dependency Injection, DI).

Afin de mieux comprendre ce tutoriel, vous devez avoir lu le tutoriel sur le design pattern fabrique.

Un peu de vocabulaire

  • Dépendance : On parle de dépendance lorsqu'une classe exige la présence de tout ou partie du code d'une autre classe.
  • Inversion de contrôle : Dans les grands framework comme Symfony, l'inversion de contrôle est le fait de donner au framework tout le contrôle sur le flot d'exécution. Le développeur délègue du pouvoir au framework afin de se simplifier la vie. Le développeur indique au framework un ensemble de paramètres (chemin vers les fichiers, préfixe de classes, plugins etc...) et c'est le framework qui gère seul l'ensemble.

L'injection de dépendances

Le principe de l'injection de dépendances consiste à ne pas marquer en dur de dépendances dans le code, elles seront créées lors de l'exécution.

Dans le tutoriel sur le design pattern fabrique je vous ai présenté une méthode pour accélérer le changement de base de données dans votre application.

Voici en gros la situation dans laquelle nous nous étions arrêtés :

FabriqueGenerale.php

abstract class FabriqueGenerale { public static function getBase() { return (new MySQL()); } public static function getReponse() { return (new Nombre('42')); } } /* Dans le reste du code j'ai donc : */ $base = FabriqueGenerale::getBase(); $base->executer($requete); ... $nombre = FabriqueGenerale::getReponse(); echo $nombre; // 42

Et bien figurez vous qu'il suffit de changer très peu de choses au code ci-dessus pour arriver au principe de l'injection de dépendances.

Dans ce code la classe FabriqueGenerale a explicitement des dépendances avec les classes MySQL et Nombre. Ce que nous allons faire, c'est utiliser un fichier de configuration pour créer ces dépendances dynamiquement.

Voici un exemple de fichier de configuration :

Configuration.txt

base.type = "MySQL" base.name = "dependency_injection" base.login = "Tuto2Dev" base.password = "pass" reponse.valeur = "42" reponse.type = "Nombre" ...

Comme vous pouvez le voir, tout est paramétrable depuis ce fichier.

Bien évidemment, pour l'utiliser, il nous faut une classe qui lit ce fichier et le transforme en code exploitable. Le but de ce tutoriel n'étant pas de développer un interpréteur de fichiers de configuration, passons directement à la suite.

Voici tout de même un exemple de résultat d'interpréteur :

Script.php

$conf = Interpreteur::interprete('Configuration.txt'); var_dump($conf); /* Résultat : $conf['base']['type'] = "MySQL"; $conf['base']['nom'] = "dependency_injection"; ... $conf['reponse']['valeur'] = "42"; $conf['reponse']['type'] = "Nombre"; */

Maintenant la partie la plus intéressante : nous allons construire dynamiquement les dépendances.

Par analogie avec la FabriqueGenerale citée plus haut :

  • Une méthode est nommée "service".
  • La classe est nommée "conteneur de services".

Le vocabulaire est différent mais c'est le même principe !

Le conteneur et ses services sont construits grâce à un Constructeur (Builder en anglais). Comme pour l'interpréteur, nous ne nous attarderons pas dessus.

Voici à quoi pourrait ressembler le service Reponse :

Conteneur.php

class Conteneur { public static function getServiceReponse() { $type = $conf['reponse']['type']; $valeur = $conf['reponse']['valeur']; return new $type($valeur); } }

Miracle ! Nous avons réussi à casser la dépendance explicite avec la classe Nombre. Cette dépendance est injectée dynamiquement.

A l'exécution, le code ci-dessus est équivalent à celui-ci :

Conteneur.php

class Conteneur { public static function getServiceReponse() { return ( new Nombre('42') ); } }

Et voilà, vous venez de comprendre le principe de l'injection de dépendances !

Bien sûr l'exemple est extrêmement basique, ne croyez pas tout savoir après avoir lu ce tutoriel. Ce qui fait la force de l'injection de dépendances c'est surtout les outils grâce auxquels elle est utilisée. La connaissance de ces outils ne peut qu'être bénéfique pour vos futurs développements. Je vous conseille de regarder du côté du Symfony Dependency Injection Component. Ce composant est totalement indépendant du reste du framework Symfony, vous pouvez donc l'utiliser dans n'importe quelle application.

Pour le fun, je vous présente le conteneur le plus petit au monde (il tient dans un tweet) : Twittee.

Twittee

class Container { protected $s=array(); function __set($k, $c) { $this->s[$k]=$c; } function __get($k) { return $this->s[$k]($this); } }

L'injection de dépendances est le design pattern le plus utilisé dans les grands frameworks comme Symfony. Il permet de rendre le code modulable, les parties indépendantes et décharge le développeur de nombreuses tâches, offrant ainsi toute sa puissance à l'inversion de contrôle.