Le design pattern Visiteur

Comment ajouter une action sur un objet sans modifier sa classe ?

Le but de ce tutoriel est d'expliquer le design pattern Visiteur (Visitor en anglais).

Le Visiteur permet d'externaliser et de centraliser des actions à effectuer sur des objets qui n'ont pas forcément de liens entre eux. Ces actions ne seront pas implémentées directement dans la classe de ces objets, mais dans des classes externes.

Exemple : nous souhaitons ajouter un mode débogage sur un ensemble d'objets. Nous ne souhaitons / pouvons pas intégrer les fonctions de debug directement dans les classes concernées.

Tout d'abord, prenons des exemples de classes que le visiteur devra savoir débuguer :

Les classes à débuguer

interface IVisitable { void accept(IVisitor visitor); } class Dog extends Mammal implements IVisitable { public String breed = "chihuahua"; public void accept(IVisitor visitor) { visitor.visit(this); } } class Human extends Mammal implements IVisitable { public String gender = "male"; public void accept(IVisitor visitor) { visitor.visit(this); } } class Book implements IVisitable { public String color = "red"; public void accept(IVisitor visitor) { visitor.visit(this); } }

Nous devons maintenant écrire un visiteur qui centralisera toutes les actions de debug et affichera pour chaque type d'objet ses propriétés.

Le visiteur

interface IVisitor { void visit(IVisitable o); void visit(Dog o); void visit(Human o); void visit(Book o); } class DebugVisitor implements IVisitor { public void visit(Dog o) { System.out.println("Breed : " + o.breed); } public void visit(Human o) { System.out.println("Gender : " + o.gender); } public void visit(Book o) { System.out.println("Color : " + o.color); } public void visit(IVisitable o) { System.out.println("Not implemented yet"); } }

main.java

public class MainRun { public static void main(String[] args) { DebugVisitor visitor = new DebugVisitor(); Dog dog = new Dog(); /* Display = Breed : chihuahua */ dog.accept(visitor); Human human = new Human(); /* Display = Gender : male */ human.accept(visitor); Book book = new Book(); /* Display = Color : red */ book.accept(visitor); } }

Et voilà nous avons réussi à ajouter une action de debug sur plusieurs types d'objets sans toucher à leur classe. Bien sûr ceci a été possible car les classes à débuguer acceptent n'importe quel visiteur. Maintenant si vous souhaitez ajouter une action pour exporter l'objet en XML, l'imprimer et bien d'autres, il vous suffit de créer un visiteur concret implémentant IVisitor (PrintVisitor, XmlExportVisitor ...). Si vous vous demandez pourquoi nous avons besoin de faire book.accept(visitor) au lieu d'appeler directement visitor.visit(book), je vous propose de lire mon tutoriel sur le design pattern double dispatch dont le visiteur est un cas particulier.