Archive for the 'Alt.net Foundations' Category

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 | No responses yet

Slides de la présentation Domain Driven Design

Julien on déc 3rd 2008

Voici les slides que Gauthier et moi-même avons utilisé pour notre présentation sur le Domain Driven Design à Alt.net :

N'hésitez pas à les réutiliser si cela peut vous être utile ! N'hésitez pas non plus à nous laisser un petit mot si c'est le cas ;-) Juste par curiosité :-).

Enfin, petit message personnel : Je cherche du boulot ! Donc si vous montez une équipe agile de la mort qui tue, contactez moi!

Filed in Alt.net Foundations, Domain Driven Design, Rencontres Alt.net | 3 responses so far

Pourquoi choisir la composition plutôt que l’héritage

Julien on mai 30th 2008

Un ami m'a récemment demandé si je pensais qu'il était acceptable d'avoir un héritage ayant une profondeur de 10 classes. Je ne pense pas que cela le soit. Si vous prenez une classe en bas de la hiérarchie, une modification sur n'importe laquelle des 9 classes supérieures peut introduire une régression. Cela peut rapidement devenir un sacré cauchemar à maintenir!

La plupart du temps, si vous avez une hiérarchie de ce type dans votre application, c'est que vous avez choisi de privilégier l'héritage sur la composition. C'est probablement aussi un "code-smell" indiquant que votre classe fait trop de chose. Afin d'éviter cela, laissez moi vous montrer comment il est possible d'étendre une classe sans utiliser l'héritage.

Partons du principe que nous commençons avec le code suivant. Je sais que c'est un example très basique, mais cela sera suffisant pour notre démonstration.

  1. abstract class TripBase
  2. {
  3. private readonly string _from;
  4. private readonly string _to;
  5.  
  6. public string From
  7. {
  8. get { return _from; }
  9. }
  10.  
  11. public string To
  12. {
  13. get { return _to; }
  14. }
  15.  
  16. protected TripBase(string from, string to)
  17. {
  18. _from = from;
  19. _to = to;
  20. }
  21.  
  22. public abstract DateTime CalculateEstimatedArrivalTime();
  23. }
  24.  
  25. class CarTrip : TripBase
  26. {
  27. public CarTrip(string from, string to)
  28. : base(from, to)
  29. {
  30. }
  31.  
  32. public override DateTime CalculateEstimatedArrivalTime()
  33. {
  34. return DateTime.Now.AddHours(15);
  35. }
  36. }
  37.  
  38. class PlaneTrip : TripBase
  39. {
  40. public PlaneTrip(string from, string to)
  41. : base(from, to)
  42. {
  43. }
  44.  
  45. public override DateTime CalculateEstimatedArrivalTime()
  46. {
  47. return DateTime.Now.AddHours(2);
  48. }
  49. }

Nous avons une classe abstraite, BaseTrip, et nous voulons l'étendre en ayant 2 sous-classes: CarTrip et PlaneTrip. Dans une application réelle, nous aurions un algorithme pour calculer ou récupérer le temps de transport. Il serait d'ailleurs probablement très différent pour chaque classe. Dans notre exemple, afin de simplifier, nous retournons juste une valeur codée en dur.

Ce code n'est pas mauvais, loin de là. Cependant il est également loin d'être parfait. Par exemple, sommes-nous sur que le calcul du temps de transport entre 2 villes est la responsabilité de nos classes CarTrip et PlaneTrip? En effet, si nous voulons ajouter une nouvelle classe MotocycleTrip qui aurait des propriétés différentes mais le même algorithme de calcul de temps de transport que CarTrip, nous sommes condamné à extraire une nouvelle super-classe abstraite que nous appelerions par exemple RoadTrip... Cela semble beaucoup de travail pour pas grand chose...

Voici une version refactorisée de ces classes, utilisant la composition plutôt que l'héritage:

  1. class Trip
  2. {
  3. private readonly string _from;
  4. private readonly string _to;
  5. private readonly ITransportationMode _transportationMode;
  6.  
  7. public string From
  8. {
  9. get { return _from; }
  10. }
  11.  
  12. public string To
  13. {
  14. get { return _to; }
  15. }
  16.  
  17. protected Trip(string from, string to, ITransportationMode transportationMode)
  18. {
  19. _from = from;
  20. _to = to;
  21. _transportationMode = transportationMode;
  22. }
  23.  
  24. public DateTime CalculateEstimatedArrivalTime()
  25. {
  26. return DateTime.Now.Add(_transportationMode.CalculateTransportationTimeBetween(_from, _to));
  27. }
  28. }
  29.  
  30. internal interface ITransportationMode
  31. {
  32. TimeSpan CalculateTransportationTimeBetween(string from, string to);
  33. }
  34.  
  35. class CarTransportationMode : ITransportationMode
  36. {
  37. public TimeSpan CalculateTransportationTimeBetween(string from, string to)
  38. {
  39. return new TimeSpan(15, 0, 0);
  40. }
  41. }
  42.  
  43. class PlaneTransportationMode : ITransportationMode
  44. {
  45. public TimeSpan CalculateTransportationTimeBetween(string from, string to)
  46. {
  47. return new TimeSpan(2, 0, 0);
  48. }
  49. }

En quelques mots, j'ai extrait les calculs dans leurs propres classes qui implémentent l'inferface ITransportationMode. Les instances sont ensuite injectées par le constructeur (mais nous pourrions également les injecter par un setter ou une méthode).

Cette implémentation améliore le code de plusieurs façons:

  • Le calcul du temps de transport est réalisé dans une nouvelle classe, cela ne pollue donc plus la classe Trip. Il est beaucoup plus facile d'ajouter de nouveaux algorithmes de calculs, il suffit d'ajouter de nouvelles implémentation de ITransportationMode. Il est donc possible de les faire évoluer indépendamment de la classe Trip, ce qu'on appelle en anglais la "Separation of Concerns" (honnêtement, je ne sais pas comment traduire ce terme!).
  • Nous pouvons maintenant configurer notre classe Trip. Avant cela, il était nécessaire de créer une nouvelle instance d'une sous-classe de TribBase pour modifier la façon dans le calcul était fait. Maintenant il nous suffit juste de changer l'instance de ITransportationMode tout en gardant le même objet Trip.
  • Nous pouvons avoir plusieurs sous-classes de Trip qui utiliseraient le même algorithme sans introduire une nouvelle classe abstraite (comme RoadTrip par exemple). En conséquent, la composition aide à garder nos hiérarchies de classes aussi plates que possible.

Filed in Alt.net Foundations | No responses yet

3 acronymes pour améliorer la qualité de votre code

Julien on avr 25th 2008

Pour ce premier billet d'une longue série (je l'espère du moins) sur les bonnes pratiques de développement, les méthodes agiles, et les valeurs défendues par Alt.Net, je vous propose de vous faire (re)découvrir 3 acronymes qu'il convient d'appliquer quotidiennement.

DRY - Don't Repeat Yourself:
L'acronyme DRY se traduit en français par "Ne vous répétez pas". Autrement dit, cet acronyme rappelle qu'il ne faut pas avoir des lignes de code identiques dupliquées à plusieurs endroits. En effet, les blocs de codes dupliqués devrait être déplacés dans une fonction ou dans une classe afin de pouvoir être réutilisables partout ailleurs. DRY permet donc une plus grande abstraction et diminue la complexité. La maintenabilité de l'application est aussi améliorée dans le sens ou les modifications du code n'auront plus à être reporté à différents endroits.

KISS - Keep It Simple, Stupid:
En développement, il est simple de faire compliqué et compliqué de faire simple! KISS, qui se traduit par "Gardez le code simple, stupide" rappelle ce grand principe. Malheureusement, il n'y a pas de recette miracle pour appliquer KISS. Il faut par exemple garder un code explicite, un niveau d'abstraction correct, ne pas optimiser prématurément, utiliser des patterns reconnus plutôt que de réinventer la roue, utiliser la composition plutôt que l'héritage pour réduire la taille des classes et promouvoir la réutilisation (DRY!), ou encore adopter la "programmation basé sur les interfaces" et l'inversion de contrôle. Evidemment, cette liste est non exhaustive et chaque point mériterait son propre billet!

YAGNI - You're Ain't Gonna Need It!:
YAGNI, ou, en français, "Vous n'allez pas en avoir besoin!" est une pratique très chére aux méthodes agiles. Le but avoué de YAGNI est d'éviter de développer quoique ce soit qui ne soit pas nécessaire maintenant. Quelques exemples:

  • il ne sert à rien de développer des fonctionnalités qui n'ont pas été demandées par les utilisateurs, sous peine de faire quelque chose de complètement inutile et de perdre son temps.
  • Attention de ne pas inventer quelque chose d'extrèmement puissant/modulaire/extensible/(ajouter votre mot ici) par rapport au besoin (KISS!). Attendez que le besoin se manifeste réellement pour commencer à développer quelque chose de plus complexe. Si vous le faites trop tôt, il est tout à fait possible que les priorités changent en cours de route et que tout soit rendu bancal par de nouveaux développements.
  • N'optimisez pas prématurément, sous peine de passer des heures à gagner 100ms sur une partie du système qui sera en fin de compte modifiée 2 semaines plus tard.

En résumé: DRY! KISS! YAGNI!

Filed in Alt.net Foundations | One response so far

Gardez vos objets dans un état consistent

Julien on avr 12th 2008

Une part d'un bon design orienté objet est de s'assurer que ses objets sont toujours dans un état correct.

Par exemple, dans le monde de la finance, nous avons des instruments financiers appelés "Dérivés". D'après Wikipedia:

Un produit dérivé est un instrument financier dont la valeur fluctue en fonction de l'évolution du taux ou du prix d'un produit appelé sous-jacent.

Par conséquent, dans le monde réel, un produit dérivé ne peut exister sans la présence du produit sous-jacent. Un future sur le CAC 40 n'aurait pas pu être créé sans que le CAC 40 existe auparavant. Si nous voulons représenter un produit dérivé dans une application, nous devons donc nous assurer que nous suivons la même règle.

Assumons que nous avons la classe suivante:

  1. class Derivative
  2. {
  3. private string _name;
  4. private Instrument _underlyingInstrument ;
  5.  
  6. public string Name
  7. {
  8. get { return _name; }
  9. }
  10.  
  11. public Instrument UnderlyingInstrument
  12. {
  13. get { return _underlyingInstrument; }
  14. }
  15.  
  16. public Derivative(string name, Instrument underlyingInstrument)
  17. {
  18. _name = name;
  19. _underlyingInstrument = underlyingInstrument;
  20. }
  21. }

Un nouveau développeur arrivant sur un projet associera notre classe Derivative au concept financier associé. A priori, il n'aura pas besoin de regarder le code de la classe et il s'attendra à ce que la propriété UnderlyingInstrument retourne un objet non-null. Cependant, l'implémentation courante de le garanti pas. En réalité, cette classe peut actuellement être utilisé pour véhiculer le nom d'un dérivé sans l'associer à son produit sous-jacent. Si nous voulions faire cela, nous devrions alors créer une nouvelle classe dans ce but uniquement. En attendant, si nous voulons que notre code reste dans un état maintenable, nous devons nous assurer que chaque objet de type Derivative sera construit correctement. Sans cela, chaque personne pourra faire un usage différent et potentiellement incompatible de la classe.

Dans le cas présent, s'assurer que notre objet est dans un état correcte peut se faire très facilement. Nous devons juste appliquer une pincée de "Design By Contract". Notre constructeur deviendra alors:

  1. public Derivative(string name, Instrument underlyingInstrument)
  2. {
  3. if(name == null || name.Length == 0)
  4. {
  5. throw new Exception("The name of the derivative can't be null or empty");
  6. }
  7. else if(underlyingInstrument == null)
  8. {
  9. throw new Exception("A derivative must have an underlying instrument");
  10. }
  11.  
  12. _name = name;
  13. _underlyingInstrument = underlyingInstrument ;
  14. }

Maintenant, nous sommes certain qu'un collègue développeur ne va pas utiliser la classe de façon étrange :)

Évidemment, il est possible d'améliorer cet exemple de façon significative avec différents frameworks et techniques (ce qui inclus l'appel à Debug.Assert ou le développement de sa propre classe d'assertion). Par exemple, J'écrirai idéalement le code de la façon suivante:

  1. public Derivative(string name, Instrument underlyingInstrument )
  2. {
  3. Guard.Against(name == null, "The name of the derivative can't be null");
  4. Guard.Against(name.Length == 0, "The name of the derivative can't be empty");
  5. Guard.Against(underlyingInstrument == null, "The underlying instrument of the derivative can't be null");
  6.  
  7. _name = name;
  8. _underlyingInstrument = underlyingInstrument;
  9. }

Mais c'est un sujet pour un autre billet!

Filed in Alt.net Foundations | No responses yet