Design Pattern Visiteur : principe et exemple d'implémentation
Découvrez le Design Pattern Visiteur : une méthode efficace pour séparer algorithmes et structures de données, avec exemples en PHP et Java.
Cette semaine je vais vous présenter un nouveau design pattern que je trouve assez intéressant : le design pattern visiteur.
Le pattern Visiteur (Visitor) permet de séparer de manière claire un algorithme d’une structure de donnée. L’algorithme n’est donc pas implémenté dans la classe mais dans des classes externes.
On limite ainsi tout couplage entre les données et leurs traitements, ce qui permet d’ajouter des traitements à nos structures de données sans avoir à les modifier. De plus, la classe externe est informée du type exact de l’objet qu’elle visite à l’aide du principe du double dispatch (bon, sauf en PHP où la signature des méthodes ne prend pas en compte le type des entrées).
Qu’est-ce que le Design Pattern Visiteur ?
Principe et avantages
Le design pattern Visiteur permet de séparer l’algorithme du modèle de données. Il consiste à appliquer un algorithme sur une structure de données sans modifier cette structure. L’algorithme est encapsulé dans une classe distincte, appelée visiteur, qui peut agir sur divers types d’objets visités, tout en préservant la flexibilité du code.
Le concept de double dispatch
Le double dispatch est essentiel au fonctionnement du pattern. Il permet à un objet de connaître son type exact à runtime et d’exécuter des méthodes adaptées au type de l’objet qu’il visite.
Implémentation du Design Pattern Visiteur
Bien que souvent évoqué comme complexe, ce design pattern est relativement simple. Notre classe visitable (c’est-à-dire notre structure de données) possède une méthode accept qui nous permet d’injecter notre visiteur (on précise une interface correspondant aux visiteurs voulus en type d’entrée).
En même temps, les visiteurs concrets implémentent une interface qui contient une méthode visit par type d’objet visitable. Lors de l’utilisation, le typage dynamique permet d’appeler la bonne méthode — c’est ce qu’on appelle le double dispatch.
Dans notre objet visitable, on appellera donc la méthode visit de notre visiteur en lui passant l’objet lui-même.
Cas d’usage en PHP
Pour cet article, j’ai d’abord implémenté une solution en PHP avant de me rendre compte que le typage faible, ainsi que la façon dont est calculée la signature des méthodes, nous empêche de redéfinir une méthode avec des types différents. Cela signifie qu’en PHP, nous ne pouvons pas pleinement bénéficier des avantages du double dispatch. Toutefois, cet exemple simplifié aide à comprendre le concept.
Grace au design pattern visiteur nous allons mettre en place un système de rendu d’un document. Notre document sera un simple objet contenant un tableau clef-valeur. Le but est d’afficher le contenu de l’objet de plusieurs façon différente : plain text, html et xml. Pour faire cela une solution triviale serait de faire une classe renderer et dedans créer une méthode par type de rendu. Lors d’un ajout/suppression de méthode de rendu on éditerai ce fichier. Les deux principaux inconvénient de cette méthode a mon gout sont que :
- on va avoir une classe assez volumineuse : difficulté de maintenance
- pour l’ajout d’une méthode on modifie la classe du coup il faut la recompiler : temps de compilation croissant avec la taille de la classe.
Nous allons donc utiliser le design pattern visiteur. Chaque type de renderer sera une classe concrété ayant le rôle de visiteur et notre document (ici ConfigDocument) aura le rôle de classe visitable.
Ainsi pour gérer les différents types de rendu on aura juste a injecter le bon renderer via accept puis récupérer la sortie au niveau de notre renderer. Le fait d’avoir du html, xml ou autre ne dépends que du renderer que nous avons injecter. Si cela vous semble déjà merveilleux, je vous conseille de lire attentivement la suite.
Cas d’usage en Java
En java la principale modification est que chaque visiteur contient plusieurs méthodes accept : une part type d’objet quelle peut visiter. De cette façon on peut exécuter du code différent selon le type d’objet qui est visité et ceci au runtime. Voici un exemple simple pour illustrer ceci :
Conclusion
- Le design pattern visiteur permet de séparer les algorithmes des structures de données sur lesquels ils sont appliqué
- A l’aide du système de double dispatch les objets visiteurs peuvent avoir des comportements différents en fonction de l’objets qu’ils visitent
C’est tout pour ce design pattern, en espérant le voir plus souvent utilisé même si on y pense pas tout le temps. J’espère que vous comprenez un peu mieux l’intérêt du visiteur et surtout comment le mettre en oeuvre. Si ce n’est pas le cas, je répondrai aux questions dans les commentaires.
comments powered by Disqus