[DUMD 8/24] Le design pattern Decorator

Auteur du billet de blog : Nicolas Hilaire - Neotech Solutions

Nicolas Hilaire

Consultant .NET
  Publié le lundi 14 novembre 2016

Artisan logiciel particulièrement intéressé par les technologies .NET. Polyvalent et curieux, je suis néanmoins à l'écoute des autres technologies du marché. MVP (Microsoft Most Valuable Professional) de 2007 à 2014, je suis également auteur d'un ouvrage pour apprendre le C#, à destination des débutants et de plusieurs MOOCs sur le C#, Windows Phone ou ASP.NET MVC...

Huitième billet sur comment devenir un meilleur développeur. Pour retrouver le sommaire ainsi que tous les liens, rendez-vous sur le premier billet.

 

En général, on commence les design pattern par le Singleton car il est très simple à apprendre et à comprendre. Sauf qu'utiliser un Singleton n'est souvent pas la meilleure solution à un problème et il est parfois décrié comme un anti-pattern. Il a beau être très simple, le plus compliqué est de savoir dans quelle situation il est vraiment judicieux de l'utiliser.

Donc, je n'en parlerai pas plus que ça, et je vais commencer par le patron de conception décorateur. Promis, je ne vais pas repeindre votre salon en couleur taupe et y ajouter des stickers, le décorateur est un patron de conception très intéressant permettant d'ajouter des responsabilités à un objet dynamiquement. Il est très pratique pour respecter les principes de responsabilité unique et d'ouverture/fermeture dont je n'ai pas encore parlé et que je présenterai dans de futurs billets.

Et tout ça, sans héritage ! Voyons donc comment il marche.

 

Le décorateur en action

Pour cela, reprenons notre exemple du calculateur de panier. Votre chef qui n'a de cesse de vous donner du boulot supplémentaire voudrait que vous mesuriez le temps de calcul du panier.

Facile me diriez-vous, grâce à la classe Stopwatch du framework .NET :

 

 

Chef : Ah, mais je voudrais aussi savoir combien de fois est appelée cette méthode, histoire de montrer au big-chef que notre travail est utile.

Facile encore :

 

 

Pour la simplicité de l'exemple, qui fonctionnellement ne va rien vous apporter, j'ai juste simulé une méthode dont on se moque de l'implémentation.

Chef : Ah ouais, mais t'as oublié de faire pareil dans la classe CalculateurDePanierEnSoldes, et en plus ce n'est pas pareil parce que j'ai juste besoin de mesurer le nombre d'appel, le temps d'exécution n'est pas utile ici.

On fait quoi ? Copier-coller ? Duplication ? On arrête l'informatique et on va ouvrir une brasserie ? :)

Il est plutôt temps de réfléchir et d'arrêter de faire du code moisi !

 

Nous allons repartir de notre classe de calcul initiale :

 

 

Puis, nous allons lui ajouter des fonctionnalités en encapsulant ce calcul, tout en conservant ce contrat de calcul. Il suffit de créer une nouvelle classe qui accepte un ICalculateur et qui ajoute son bout de code tout en déléguant le reste au calculateur qui lui a été fourni. Cela ressemble à ça :

 

 

La ligne 13 correspond à la délégation du calcul, et le reste au code vraiment utile à la mesure de temps.


Et si nous souhaitons construire un objet "calculateur" qui sera capable de calculer le montant du panier et de mesurer son temps d'exécution, nous aurons juste à faire :

 

 

Et pour rajouter le compteur d'utilisation ? Facile, on applique le même principe :

 

 

Ainsi, nous pourrons créer nos deux paniers :

 

 

Et voilà, nous avons deux compositions différentes de fonctionnalités pour nos deux paniers, sans duplication de code et finalement sans avoir modifié la première implémentation de la méthode de calcul.

C'est toute la beauté du pattern Décorateur.

 

Conclusion

N'hésitez pas à aller consulter d'autres exemples du décorateur, par exemple sur la page anglaise de Wikipédia, où vous aurez également le diagramme UML dont je vous fais grâce dans ce billet.

Le design pattern décorateur est très intéressant et permet de rajouter dynamiquement des fonctionnalités à une classe.

On pourrait se dire par contre qu'avec un décorateur on va avoir tendance à multiplier rapidement le nombre de petites classes qui ne font pas grand chose, comme dans notre exemple. Mais en fait, c'est plutôt une bonne chose, cela permet de respecter le principe de responsabilité unique (oui, je sais, je n'en ai pas encore parlé !). Les classes sont centrées sur un problème et l'adressent spécifiquement, en réduisant le risque d'erreur.

Vous pourrez me dire également que la construction des objets de calcul devient assez complexe, ce qui est vrai. Cependant, ce problème est résolu grâce à d'autres design pattern de construction, comme la fabrique. Et si nous y jetions un œil dans le prochain billet ?

Commentaires