Voici un exemple complet utilisant le pattern Visitor pour gérer les capacités des animaux (nager, voler, courir) sans utiliser de instanceof ou de casting. Ce pattern est particulièrement utile lorsqu’on a une hiérarchie de classes stable (comme Animal) et que l’on veut ajouter de nouvelles spécificités comme nager, voler sans modifier les classes existantes !
instanceof ou de casting
Étape 1 : Définir l’interface Animal et la méthode accept(). Chaque classe concrète d’animal devra implémenter une méthode accept() qui permettra au visiteur de visiter l’animal.
// interface Animal avec la méthode accept() public interface Animal { void accept(AnimalVisitor visitor); }
Cette interface définit les méthodes de visite pour chaque type d’animal concret.
// interface du visiteur public interface AnimalVisitor { void visit(Chien chien); void visit(Oiseau oiseau); void visit(Poisson poisson); }
Chaque classe d’animal implémente Animal et la méthode accept().
Classe Chien :
public class Chien implements Animal { private String nom; public Chien(String nom) { this.nom = nom; } @Override public void accept(AnimalVisitor visitor) { visitor.visit(this); // passe l'instance courante au visiteur } public void nager() { System.out.println(nom + " nage en battant des pattes !"); } public void courir() { System.out.println(nom + " court à quatre pattes !"); } public String getNom() { return nom; } }
Classe Oiseau :
public class Oiseau implements Animal { private String nom; public Oiseau(String nom) { this.nom = nom; } @Override public void accept(AnimalVisitor visitor) { visitor.visit(this); } public void voler() { System.out.println(nom + " vole dans le ciel !"); } public void courir() { System.out.println(nom + " saute en marchant !"); } public String getNom() { return nom; } }
Classe Poisson :
public class Poisson implements Animal { private String nom; public Poisson(String nom) { this.nom = nom; } @Override public void accept(AnimalVisitor visitor) { visitor.visit(this); } public void nager() { System.out.println(nom + " nage en ondulant !"); } public String getNom() { return nom; } }
Chaque visiteur concret implémente AnimalVisitor et définit le comportement pour chaque type d’animal.
public class NagerVisitor implements AnimalVisitor { @Override public void visit(Chien chien) { chien.nager(); // Un chien peut nager } @Override public void visit(Oiseau oiseau) { // Un oiseau ne nage pas (sauf exception, comme un manchot) System.out.println(oiseau.getNom() + " ne sait pas nager !"); } @Override public void visit(Poisson poisson) { poisson.nager(); // Un poisson peut nager } }
Visiteur pour faire voler les animaux :
public class VolerVisitor implements AnimalVisitor { @Override public void visit(Chien chien) { // Un chien ne vole pas System.out.println(chien.getNom() + " ne sait pas voler !"); } @Override public void visit(Oiseau oiseau) { oiseau.voler(); // Un oiseau peut voler } @Override public void visit(Poisson poisson) { // Un poisson ne vole pas System.out.println(poisson.getNom() + " ne sait pas voler !"); } }
Visiteur pour faire courir les animaux :
public class CourirVisitor implements AnimalVisitor { @Override public void visit(Chien chien) { chien.courir(); // chien peut courir } @Override public void visit(Oiseau oiseau) { oiseau.courir(); // oiseau peut "courir" (sautiller) } @Override public void visit(Poisson poisson) { // poisson ne court pas System.out.println(poisson.getNom() + " ne sait pas courir !"); } }
public class Main { public static void main(String[] args) { // liste d'animaux List<Animal> animaux = new ArrayList<>(); animaux.add(new Chien("Rex")); animaux.add(new Oiseau("Piaf")); animaux.add(new Poisson("Nemo")); // visiteurs AnimalVisitor nagerVisitor = new NagerVisitor(); AnimalVisitor volerVisitor = new VolerVisitor(); AnimalVisitor courirVisitor = new CourirVisitor(); // application des visiteurs à tous les animaux System.out.println("=== Ceux qui nagent ==="); for (Animal animal : animaux) { animal.accept(nagerVisitor); } System.out.println("\n=== Ceux qui volent ==="); for (Animal animal : animaux) { animal.accept(volerVisitor); } System.out.println("\n=== Ceux qui courent ==="); for (Animal animal : animaux) { animal.accept(courirVisitor); } } }
Résultat attendu :
=== Nageurs === Rex nage en battant des pattes ! Piaf ne sait pas nager ! Nemo nage en ondulant ! === Voleurs === Rex ne sait pas voler ! Piaf vole dans le ciel ! Nemo ne sait pas voler ! === Coureurs === Rex court à quatre pattes ! Piaf saute en marchant ! Nemo ne sait pas courir !
Ne pas utiliser quand la hiérarchie de classes change souvent (nouvelle classe d’animal fréquente) et les opérations sont simples et peu nombreuses (dans ce cas, instanceof ou une méthode générique comme faireNager() peut suffire).
Animal (interface) │ ├── Chien ├── Oiseau └── Poisson │ ▼ AnimalVisitor (interface) │ ├── NagerVisitor ├── VolerVisitor └── CourirVisitor
On va imaginer qu’un réparateur fait des choses différentes selon les objets, ici, des jouets cassés.
Votre enfant a une boîte de jouets cassés avec :
Vous, le “Visteur” alias le réparateur, vous allez :
Chaque jouet accepte la visite du réparateur, mais le réparateur fait quelque chose de différent pour chaque type de jouet de votre enfant !
En java on pourrait écrire :
Interface :
// interface pour les jouets (ils "acceptent" un visiteur) interface Jouet { void accepter(Visiteur visiteur); }
Classe Voiture :
// différents jouets class Voiture implements Jouet { public void accepter(Visiteur visiteur) { visiteur.visiter(this); // "Moi, la voiture, je me fais visiter !" } }
Classe Avion :
class Avion implements Jouet { public void accepter(Visiteur visiteur) { visiteur.visiter(this); // "Moi, l'avion, je me fais visiter !" } }
Classe Robot :
class Robot implements Jouet { public void accepter(Visiteur visiteur) { visiteur.visiter(this); // "Moi, le robot, je me fais visiter !" } }
Interface Visiteur :
// interface du visiteur (ce qu'il fait pour chaque jouet) interface Visiteur { void visiter(Voiture voiture); void visiter(Avion avion); void visiter(Robot robot); }
Classe Réparateur :
// visiteur concret : le réparateur (vous) class Reparateur implements Visiteur { public void visiter(Voiture voiture) { System.out.println("Je répare la voiture : je change les roues ! ""); } public void visiter(Avion avion) { System.out.println("Je répare l'avion : je vérifie les ailes ! "); } public void visiter(Robot robot) { System.out.println("Je répare le robot : je change les piles ! "); } }
Classe Main :
// Utilisation public class Main { public static void main(String[] args) { Jouet[] boiteAJouets = {new Voiture(), new Avion(), new Robot()}; Visiteur reparateur = new Reparateur(); for (Jouet jouet : boiteAJouets) { jouet.accepter(reparateur); // Chaque jouet se fait réparer différemment ! } } }