Inversion de contrôle et injection de dépendances

Dans le cas d'une application web par exemple, l'approche "classique" consiste à écrire un programme qui démarre, charge un environnement, lit une requête d'un client, la traite, met en forme le résultat, le renvoie au client, attend la requête suivante, etc, avant de s'arrêter, idéalement en sauvegardant proprement son état et son environnement. L'application contrôle tout de A à Z. L'inversion de contrôle consiste à déléguer le plus gros des tâches à une application tierce, généralement un framework, et à juste fournir à cette application des objets à utiliser dans certains cas. C'est le framework qui décidera de les instancier et d'appeler leurs méthodes en fonction des besoins. C'est lui qui a le contrôle et utilise vos objets. Cela permet de se concentrer sur l'écriture des objets métiers et de déléguer le reste des traitements à un programme tiers. Idéalement,les objets pourront être utilisés quelque soit le framework sous-jacent.

L'injection de dépendances est un exemple d'inversion de contrôle. Si un objet A utilise un objet B, on laisse le framework qui contrôle l'exécution de A instancier B et le passer à A au moment de l'exécution. Un exemple classique est une connexion à une base de données. Si un objet à besoin d'une telle connexion, il n'a pas à la créer lui-même. C'est le framework qui la lui passera au moment opportun. Cela permet de ne plus gérer les dépendances entre objets au moment de l'écriture du code, mais de son exécution, ce qui rend les différentes briques fonctionnelles plus indépendantes les unes des autres, d'où un code plus ré-utilisable.

Crafty

Crafty est un programme qui prétend faciliter l'injection de dépendances en PHP. J'avoue que je ne l'ai pas testé, mais la documentation est intéressante.

Soit un objet O qui utilise des objets D1, D2, D3... Vous pouvez passer les différents objets dépendants au constructeur... Mais ca devient vite lourd s'il y en a beaucoup. Vous pouvez aussi définir des setters et les appeler:

class toto {
 private $_obj1;
 private $_obj2;
 private $_obj3;
 public function setObj1(titi $o) {
   $this->_obj1 = $o;
 }
 public function setObj2(tata $o) {
   $this->_obj2 = $o;
 }
 public function setObj3(tutu $o) {
   $this->_obj3 = $o;
 }
 (...)
}
$plouf = new toto();
$plouf->setObj1(new titi());
$plouf->setObj2(new tata());
$plouf->setObj3(new tutu());
(...)

Crafty permet de remplacer les dernières lignes, c'est à dire l'instanciation des objets dont dépend l'objet principal et leur passage à cet objet, par un simple appel:

$plouf = $crafty->create('toto');

Enfin, pas si simple que cela car il faut bien évidemment lui expliquer quelles sont les dépendances entre les classes. On utilise pour cela des fichiers de configuration, qui peuvent être en XML, en YAML, ou en PHP, et qui décrivent l'ensemble des classes, les paramètres à passer à leurs constructeurs respectifs et les dépendances entre elles. Dans l'exemple ci-dessus, on indiquera que toto a besoin de titi, tata et tutu et que ces dépendances doivent lui être passées via des setters. Dernier point, un paramètre dans le fichier de configuration permet de préciser s'il faut créer de nouvelles instances des objets liés à chaque création d'un objet principal, ou si on utilise les mêmes.

Si mon exemple n'a aucun intérêt, je garde le lien vers Crafty pour le tester à l'occasion sur un "vrai" projet.

Ah, et pour l'essayer, il faut aller le chercher dans svn