Comprendre l’injection de dépendances

Julien on déc 16th 2008

On m'a récemment demandé de citer un pattern que j'utilise systématiquement dans mes projets. Question difficile dans la mesure ou un pattern ne doit justement pas être appliqué mécaniquement : son utilisation est conditionnée par le contexte.

Pourtant, apres réflexion, il y a bien un pattern que j'utilise de façon systématique : l'injection des dépendances qui est un application d'un pattern plus global appelé inversion de contrôle (Dependency Injection et Inversion of Control en anglais, ou DI et IoC).

L'inversion de contrôle est un concept assez générique. Il permet à un framework de déléguer l'implémentation et l'exécution d'un comportement à du code qui n'est pas lui même contenu dans le framework. Plusieurs façons existent pour appeler le code en question, comme :

  • La programmation orientée évenement : le code appelant le framework à la possibilité de se lier à certains évènements pour effectuer des opérations quand ces derniers sont levés. On retrouve cet architecture à tous les étages du framework .Net.
  • Le pattern "Template method" : une classe abstraite définie une fonction qui appelle un certain nombre de méthodes virtuelles. Celles ci peuvent doivent être implémentées dans une classe fille pour définir le comportement.
  • L'injection des dépendances, que je vais expliquer dans les lignes qui suivent :-)

L'injection des dépendances :

Un programme informatique comprends généralement un ensemble plus ou moins complexe de dépendances entre différentes classes. Par exemple, nous pourrions envoyer un email avec le code suivant :

  1. class Controller
  2. {
  3. public void DoStuff()
  4. {
  5. var emailSender = new EmailSender();
  6. emailSender.Send("j@j.com", "Email Subject", "This is the content !");
  7. }
  8. }
  9.  
  10. class EmailSender
  11. {
  12. public void Send(string to, string subject, string message)
  13. {
  14. // Send the email...
  15. }
  16. }

Nous venons de créer une relation de dépendance forte entre la classe Controller et la classe EmailSender. Cependant, l'envoi d'email est un service qui peut avoir de nombreuses implémentations. On pourrait par exemple avoir une implémentation utilisant la classe System.Net.Mail.SmtpClient et une seconde utilisant un prestataire externe par l'intermédiaire de web services. Qui plus est, on peut envisager d'utiliser la première implémentation en test et la seconde en production. Autrement dit, nous ferions mieux de refactorer ce code pour découpler Controller d'une implémentation spécifique d'EmailSender !

Première étape de notre refactoring, EmailSender doit implémenter un contrat (une interface) :

  1. interface IEmailSender
  2. {
  3. void Send(string to, string subject, string message);
  4. }
  5.  
  6. class EmailSender : IEmailSender
  7. {
  8. public void Send(string to, string subject, string message)
  9. {
  10. // Send the email...
  11. }
  12. }

Deuxième étape : remplacer la dépendance Controller <-> EmailSender par Controller <-> IEmailSender :

  1. class Controller
  2. {
  3. private IEmailSender _emailSender;
  4.  
  5. public void DoStuff()
  6. {
  7. _emailSender.Send("j@j.com", "Email Subject", "This is the content !");
  8. }
  9. }

A cet instant, la classe Controller ne dépend plus d'une implémentation spécifique d'EmailSender. Nous avons donc appliqué le pattern inversion de contrôle. Dernière étape : injecter l'implémentation voulue dans la classe Controller, par l'intermédiaire du constructeur :

  1. class Controller
  2. {
  3. private IEmailSender _emailSender;
  4.  
  5. public Controller(IEmailSender emailSender)
  6. {
  7. _emailSender = emailSender;
  8. }
  9.  
  10. public void DoStuff()
  11. {
  12. _emailSender.Send("j@j.com", "Email Subject", "This is the content !");
  13. }
  14. }

(Remarque : l'injection peut aussi être faite par l'intermédiaire d'une propriété)

Et ça y est, nous venons d'appliquer l'injection de dépendances !

L'utilisation de ce pattern offre plusieurs bénéfices immédiats :

  • Nous l'avons déjà vu, Controller ne dépend plus d'une implémentation spécifique. Il est donc facile de passer d'une implémentation à une autre en fonction du contexte
  • La dépendance entre Controller et IEmailSender est explicite, le code devient plus maintenable et compréhensible
  • Si la dépendance est exposée, il est possible de la remplacer par une implémentation factice pour écrire des tests unitaires
  • Enfin, la configuration des dépendances peut maintenant être centralisée et effectuée par l'intermédiaire un framework (StructureMap, Spring.Net, Castle, NInject, etc.).

Ces deux derniers points feront l'objet d'un billet prochainement :-).

Filed in Alt.net Foundations | 7 responses so far

7 Responses to “Comprendre l’injection de dépendances”

  1. joseph mascis déc 24th 2010 at 08:07 1

    pattern très utilisé effectivement , l’explication est limipide. merci

  2. Mathieu jan 15th 2011 at 05:08 2

    La mystification sur des termes complexes éloigne de la compréhension d’un mécanisme simple.

    inversion de contrôle, injection de dépendance, refactoring, contrat.

    Nous venons de proposer une option d’envoie de message à un contrôleur. C’est un aiguillage au mieux.

  3. Morgan avr 5th 2011 at 08:29 3

    C’est très clair grâce à votre explication, merci beaucoup.

  4. Florian oct 3rd 2011 at 09:51 4

    Un article bien fait, j’y vois mieux 0_0 . Merci

  5. NlC0 oct 13th 2011 at 03:12 5

    Explication simple mais efficace. Un grand merci.

  6. Voahar oct 27th 2011 at 08:56 6

    Explication simple et claire. Que du bon

  7. Damien nov 7th 2011 at 03:52 7

    Cas concret, qui me parle, explication simple et efficace, Tout bon.

Trackback URI | Comments RSS

Leave a reply