PHP : la bonne pratique

Apprendre les bonnes pratiques de programmation en PHP


précédentsommairesuivant

VI. Injection de dépendances

L'injection de dépendances est un mécanisme qui permet d'implanter le principe de l'inversion de contrôle. Il consiste à créer dynamiquement (injecter) les dépendances entre les différentes classes en s'appuyant sur une description (fichier de configuration ou métadonnées) ou de manière programmatique. Ainsi les dépendances entre composants logiciels ne sont plus exprimées dans le code de manière statique, mais déterminées dynamiquement à l'exécution.

Cette citation rend le concept plus compliqué qu'il n'y paraît. L'injection de dépendances fournit un composant avec ses dépendances que ce soit via un constructeur, des appels de méthodes ou la configuration de propriétés. C'est tout.

VI-A. Concepts de base

Nous pouvons démontrer le concept avec un exemple tout bête.

Imaginons que nous ayons une classe Database qui exige un adaptateur (adapter en anglais) pour communiquer avec la base de données. Nous instancions l'adaptateur à l'intérieur du constructeur et créons une dépendance forte. Cela rend les tests compliqués étant donné que la classe Database est fortement couplée à l'adaptateur.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
<?php
namespace Database;

class Database
{
    protected $adapter;

    public function __construct()
    {
        $this->adapter = new MySqlAdapter;
    }
}

class MysqlAdapter {}

Ce code peut être refactorisé pour utiliser l'injection de dépendances et ainsi délier l'adaptateur de la classe.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
<?php
namespace Database;

class Database
{
    protected $adapter;

    public function __construct(MySqlAdapter $adapter)
    {
        $this->adapter = $adapter;
    }
}

class MysqlAdapter {}

Maintenant nous fournissons à la classe Database les dépendances nécessaires en argument au lieu de les créer nous-même. Nous pouvons même créer une méthode qui accepterait les paramètres des dépendances afin de les fixer nous-même, ou si la propriété $adapter était public, nous pourrions la définir directement.

VI-B. Problèmes complexe

Si vous avez déjà lu des articles sur l'injection de dépendances, alors vous avez probablement vu des termes comme « Inversion de contrôle » ou « Principe d'inversion de dépendances ». Ces termes sont les problèmes complexes que l'injection de dépendances cherche à résoudre.

VI-B-1. Inversion de contrôle

L'inversion de contrôle est, comme il le sous-entend, un système cherchant à « inverser le contrôle » en gardant le contrôle organisationnel entièrement séparé des objets. En termes d'injection de dépendances, cela signifie que l'on sépare les dépendances en les contrôlant et en les instanciant ailleurs dans le système.

Pendant des années, les frameworks PHP ont fait de l'inversion de contrôle, cependant la question est devenue : « quelle partie du contrôle doit-on inverser et vers où ? » Par exemple, les frameworks MVC fourniront généralement un super objet ou un contrôleur de base dont les autres contrôleurs doivent hériter pour avoir accès à ses dépendances. C'est ça l'inversion de contrôle, cependant, au lieu de séparer les différentes dépendances, cette méthode ne fait que les déplacer.

L'injection de dépendances nous permet de résoudre ce problème de façon plus élégante en injectant uniquement les dépendances dont nous avons besoin, quand nous en avons besoin, et ce, sans avoir à écrire en dur quelque dépendance que ce soit.

VI-B-2. Principe d'inversion des dépendances

Le principe d'inversion des dépendances (en anglais Dependency Inversion Principle) correspond au « D » dans « S.O.L.I.D. » qui est un ensemble de principes et de conceptions orientés objet. Il est dit que l'on doit « dépendre des abstractions et non des implémentations ». Cela signifie que nos dépendances doivent se faire sur des interfaces/contrats ou encore sur des classes abstraites plutôt que sur des classes « concrètes ». Nous pouvons facilement refactoriser l'exemple ci-dessus en suivant ce principe.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
<?php
namespace Database;

class Database
{
    protected $adapter;

    public function __construct(AdapterInterface $adapter)
    {
        $this->adapter = $adapter;
    }
}

interface AdapterInterface {}

class MysqlAdapter implements AdapterInterface {}

Il y a plusieurs avantages à ce que la classe Database dépende d'une interface plutôt que de son implémentation.

Imaginons que vous êtes en train de travailler dans une équipe et que l'adaptateur est écrit par un de vos collègues. Dans notre premier exemple, nous aurions d'abord attendu que notre collègue ait fini l'adaptateur avant de pouvoir l'utiliser dans nos tests unitaires. Maintenant que la dépendance correspond à une interface, nous pouvons créer un objet factice implémentant cette interface en sachant que notre collègue construira l'adaptateur en respectant le contrat de base.

Un bénéfice encore plus grand de cette méthode est que notre code est maintenant plus facilement évolutif (scalable en anglais). Si dans un an nous décidons que nous voulons migrer sur un autre type de base de données, alors nous n'avons qu'à écrire et utiliser l'adaptateur qui implémente l'interface spécifique et ainsi, nous n'avons plus besoin de refactoriser du code.

VI-C. Conteneur

La première chose que vous devriez comprendre sur les conteneurs d'injection de dépendances est qu'il ne s'agit pas de la même chose que l'injection de dépendances. Un conteneur est un moyen pratique d'implémenter l'injection de dépendances, cependant, ils peuvent être souvent mal utilisés et devenir un antipattern. Injecter un composant ID en tant que localisateur de services à l'intérieur de vos classes crée sans aucun doute une dépendance plus forte que la dépendance que vous remplacez. Cela rend aussi votre code moins transparent et finalement plus dur à tester.

La plupart des frameworks modernes ont leur propre conteneur d'injection de dépendances qui permettent de brancher vos dépendances ensemble à travers un fichier de configuration. Cela signifie en pratique que vous écrivez du code métier aussi propre et découplé que le framework sur lequel vous vous basez.


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Licence Creative Commons
Le contenu de cet article est rédigé par Josh Lockhart et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2017 Developpez.com.