1. Tout d'abord, l'auteur de la présentation incriminée précise ici, ou bien encore , que l'utilisation d'un conteneur pour faire de la DI est loin d'être indispensable.
  2. Ensuite, je suis globalement d'accord avec l'auteur du billet, mettre en place une usine à gaz à l'aide d'un autre langage pour faire de l'injection ajoute une couche de complexité quelque peu inutile, même si j'aime que la machine travaille à ma place.

J'en serais resté là si la discussion n'avait pas rebondi sur le canal IRC #openweb,

En effet, plusieurs personnes y ont débattu sur l'utilité même du concept, avant même de se demander si l'implémentation proposée était pertinante, efficace, ou logique.

Tout d'abord, il a fallu définir précisément le terme barbare d'injection de dépendance.

Faire de l'injection de dépendance, c'est créer une interface au niveau d'une classe d'objet qui permet de définir les objets que la classe doit manipuler en interne.

En terme plus old school ou has been, il ne s'agit donc ni plus ni moins que de permettre à l'utilisateur de la classe de faire de la composition dynamiquement.

Pour que les choses soient claires, voici un exemple, en pseudo code :

class A
{
protected $a = null;

public function __construct(a $a = null) # <= injection de dépendance
{
if ($a === null) $this->setA($a);
}

public function setA(a $a) <= interface publique permettant de faire de l'injection de dépendance
{
$this->a = $a;
return $this;
}

public function useA()
{
...
}
}

Bref, l'injection de dépendance basique n'est pas une nouveauté et est en réalité une bonne pratique de programmation.

Il s'agit en effet de faire du découplage à l'aide de la composition, et il n'y a donc pas lieu d'en démontrer l'utilité.

Par contre, l'injection de dépendance évoluée telle qu'elle est définie dans les design pattern, est beaucoup plus complexe car elle permet de définir les régles d'instanciations d'un objet.

Grâce à elle, le développeur peut définir que lorsqu'un objet de type A est instancié, il doit être initialisé avec des objets de type B et C, s'il est utilisé dans un objet de type D.

Plus clairement, l'injection de dépendance permet de remplacer ceci :

$B = new inheritFromBclass();
$C = new inheritFromCclass();
$D = new D();
$D->getA()->setB($B)->setC($C);

par cela :

$D = depedencyInjecter::get('D')
# l'injection de dépendance créé un objet de type D
# composé automatiquement avec un objet de type A
# lui même composé avec les objets de type B et C,
# en fonction de régles définies par le programmeur
# via un fichier de configuration, du code, etc.

Il s'agit donc de méta-programmation, puisque ce mécanisme simplifie la vie du développeur fainéant en réduisant la quantité de code à écrire.

A première vu, l'injection de dépendance n'a donc rien d'indispensable, pour peu que le développeur accepte à long terme d'avoir le bout des doigts carrés à force de taper sur son clavier.

Mais à y regarder de plus près, cela peut être intéréssant.

En effet, réaliser l'initialisation d'un objet et ensuite lui passer les objets qu'il doit utiliser pour fonctionner n'est pas très lourd lorsqu'il n'y a qu'un objet à créer de cette manière, mais si leur nombre augmente ? Ou si le code a été mal conçu ?

Par exemple, dans l'un de mes projets, j'ai un objet DB qui me permet de dialoguer avec une base de données.

Pour des raisons historiques, le code est peu optimisé et ne respecte guère les bonnes pratiques.

Cet objet est donc instancié plusieurs fois, à différents endroits, à la fois au niveau global, et à l'intérieur de classe, parfois très profondément dans une hiérarchie de classe à cause de l'héritage.

Il est donc très difficile, voir impossible, d'y accéder pour le manipuler et lui injecter les objets qu'il doit utiliser.

Je n'ai donc aucun moyen d'être certain que cet objet DB est correctement construit lorsque je fais appel à lui transitivement via d'autres objets qui l'utilise :

class DB { ... }
class A { function __construct() { ... } public function doSomethingInDB() {$DB = new DB();}
class B extends A { ... }
class C { private $B = null; function __construct(B $B) { $this->B = $B; } public function useB() { $B->doSomethingInDB; }
# Je ne dispose d'aucun moyen pour manipuler DB dans cette dernière méthode
# et lui injecter des objets.

Evidement, je pourrais corriger le code... ou je pourrais faire de l'injection de dépendance et être ainsi certain que mon objet DB est correctement construit :

class A { function __construct() { ... } public function doSomethingInDB() {$DB = depedencyInjecter::get('DB');}

Ainsi, je peux injecter ce que je veux dans mon objet DB indépendament de l'interface de la classe qui le contient, et en fonction de son contexte de création.

De même, si j'ai un grand nombre d'objets à instancier d'une certaine manière, et cela arrive souvent dans le cadre de tests unitaires, l'injection de dépendance peut me décharger d'une charge de travail peu intéressante intellectuellement, et me permettre de me concentrer sur l'essentiel.

En conclusion, l'injection de dépendance évoluée relève de la méta-programmation, dans le sens ou elle permet aux objets de se construire eux-même sans l'intervention du développeur et le décharge ainsi d'une tâche relativement ingrate et potentiellement répétive.

Elle a donc tout à fait sa place dans des projets relativement important en terme de volume de code, et je suis personnellement tout à fait prêt à sacrifier quelques cycles de processeur pour profiter de ses avantages.

Comme je l'ai dis sur #openweb, tout l'art du développeur consiste à trouver le bon compromis entre sa charge de travail et celle du code, en fonction du contexte technique, humain et économique de son projet.