Le design pattern Builder

Récemment, j’ai été confronté au fait de devoir manipuler les mêmes données pour ensuite les convertir dans trois formats différents (des formats de sous-titres en l’occurrence).

J’ai aussi eu besoin de manipuler les mêmes données pour les renvoyer en html ou en json.

Le design pattern Builder est parfait pour ça. Si on ne l’applique pas, voici ce que cela pourrait donner:

Ok, vous allez me dire: « oh, ce n’est pas dramatique! » Oui, c’est sûr. Mais imaginez que l’on doive finalement avoir un troisième rendu en csv… On revient sur le code (donc on ne respecte pas le O de SOLID) et on rajoute un if. Et en plus, comme le rendu est plus complexe, on appelle une classe qui va effectuer le rendu csv.

Et imaginez qu’il y a un quatrième rendu… puis un cinquième… et que chacun fait appel à sa propre classe de rendu (par exemple sous forme de camembert, que sais-je!). Ou qu’un jour, on décide de ne pas simplement renvoyer du json mais d’avoir un rendu plus élaboré (et donc d’appeler une classe qui va le rendre)… vous commencez à voir le souci.

Ce que je vous propose ici est un code simple mais qui va découpler les builders et rendre le contrôleur complètement dynamique sans que l’on ait un jour besoin d’y revenir.

Construire nos builders

Comme nous l’avons vu, ils sont très simples puisque le JsonBuilder renvoie juste les résultats en json alors que le HtmlBuilder fait appel à un template twig. Et bien sûr, s’il y avait un CsvBuilder, un XmlBuilder etc., ils feraient des choses plus complexes.

Ne vous étonnez pas de la fonction isFormatMatch, elle va nous servir plus tard pour déterminer à la volée le bon builder dans le contrôleur.

Notre BuilderResolver

Il est temps de déterminer la classe qui aura dans un tableau les différents builders, rajoutés à la volée par le CompilerPass, et qui retournera le builder voulu par rapport au format attendu:

Vous allez peut-être me dire: « mais comment les builders vont-ils être ajoutés à la volée? ». Pour faire cela, il faut déjà tagguer nos builders qui implémente une interface:

Ensuite, dans services.yaml (Symfony 3.4/4.1):

 

Puis le compilerPass:

et sa déclaration dans Kernel.php:

Le contrôleur

Il va appeler le BuilderResolver et lui passer en argument le format demandé dans l’uri:

Le mot de la fin

Je sais que ça a l’air plus lourd ainsi et que l’on se dit: « mais pourquoi se compliquer autant la vie? » Mais par expérience, dès qu’il commence à y avoir deux cas, il y en aura un jour un troisième, et un quatrième etc. Avec ce design pattern, vous vous assurez de ne pas modifier le code au niveau du contrôleur (à part au niveau de l’annotation pour rajouter de nouveaux formats).

Vous avez juste à créer de nouveaux builders, tous indépendants les uns des autres, et qui s’enregistreront automatiquement dans le BuilderResolver sans que vous n’ayez rien à faire.

Le code est visible sur github ici: https://github.com/jpsymfony/dp-builder

Cet article s’inspire de celui-ci: http://www.croes.org/gerald/blog/le-design-pattern-monteur-builder-en-php/687/

Rédigé par

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *