L'injection de dépendance pour les nuls

Publié le 4 Juillet 2014

Voilà un terme plutôt impressionnant. Enfin c'est ce que je me disais avant de comprendre de quoi il s'agissait mais en fait c'est un principe assez simple qui se cache derrière ces mots.

 

Commençons par un peu de code pour illustrer le principe. Prenons une classe qui utilise un service quelconque:

public class Traducteur {

  public String traduit(String phrase) {
    // TraductionService peut par exemple appeler
    // un service de traduction en ligne
    // comme par exemple Google Translate

    return GoogleTraductionService.translate(phrase)
  }

}

C'est volontairement très (trop) simple mais nous avons ici une classe (Traducteur) qui utilise une autre classe (GoogleTraductionService) pour effectuer une tâche (une traduction).

Jusqu'ici tout va bien mais nous avons introduit un couplage fort entre le traducteur et le service de traduction.

Vous ne voyez toujours pas le problème ... et bien essayons d'écrire un test unitaire pour notre traducteur :

public class TraducteurTest {

  Traducteur traducteur = new Traducteur();

  @Test
  public void testTraduction() {
    String traduction = traducteur.traduit("voiture");
    Assert.equals("car", traduction);
  }

}

 

Imaginons que notre TraductionService appelle l'api de Google Translate et qui nous soyons connecté à internet. Le test passe. Pas de problème!

Oui mais voilà imaginons que nous n'ayons plus de connection internet, le test est KO. Et oui c'est un test unitaire le but n'est pas de tester notre TranslationService et encore moins l'API de Google!

Le problème c'est que dès qu'on appelle la méthode "traduit()" on se retrouve avec notre GoogleTranslationService.

Comment faire? Tout simplement introduire une interface et passer le service au constructeur ce qui donne:

public class Traducteur {

  private TranslationService service;

  public Traducteur(TranslationService service) {
    this.service = service;
  }

  public String traduit(String phrase) {
    return service.translate(phrase);
  }

}

Voilà maintenant notre Traducteur peut fonctionner avec n'importe quel service de traduction du moment qu'on a une implementation qui respecte notre interface.

Et pour notre test ... et bien on peut écrire notre propre service :

public class TraducteurTest {

  static class TraductionServiceBouchon implements TraductionService {

    public String traduit(String phrase) {
      if ("voiture".equals(phrase)) return "car";
      else return "";
    }

  }

  TraducteurService service = new TraductionServiceBouchon();
  Traducteur traducteur = new Traducteur(service);

  @Test
  public void testTraduction() {
    String traduction = traducteur.traduit("voiture");
    Assert.equals("car", traduction);
  }

}

Et voilà nous venons d'effectuer une injection de dépendance. Nous avons injecté le service de traduction (notre dépendance) dans le constructeur du Traducteur.

Et nous avons gagné en souplesse: on peut utiliser notre traducteur avec n'importe quelle implémentation de TraductionService et notre test unitaire est vraiment unitaire.

 

Bref si vous écrivez de bon tests unitaires il y a de fortes chances que vous fassiez de l'injection de dépendance sans vraiment le savoir.

Rédigé par Bliz

Publié dans #Java

Repost 0
Pour être informé des derniers articles, inscrivez vous :
Commenter cet article

os 13/12/2016 06:15

C'est la meilleure explication que j'aie trouvée jusqu'à présent. Merci!

Alex 20/02/2015 10:46

Simple rapide effiace et pédagogue !
Merci

Charlie 13/01/2015 10:00

Merci !