Design patterns Visitor Pattern Visitor pattern example in java


Example

Visitor pattern allows you to add new operations or methods to a set of classes without modifying the structure of those classes.

This pattern is especially useful when you want to centralise a particular operation on an object without extending the object Or without modifying the object.

UML diagram from wikipedia:

enter image description here

Code snippet:

import java.util.HashMap;

interface Visitable{
    void accept(Visitor visitor);
}

interface Visitor{
    void logGameStatistics(Chess chess);
    void logGameStatistics(Checkers checkers);
    void logGameStatistics(Ludo ludo);    
}
class GameVisitor implements Visitor{
    public void logGameStatistics(Chess chess){
        System.out.println("Logging Chess statistics: Game Completion duration, number of moves etc..");    
    }
    public void logGameStatistics(Checkers checkers){
        System.out.println("Logging Checkers statistics: Game Completion duration, remaining coins of loser");    
    }
    public void logGameStatistics(Ludo ludo){
        System.out.println("Logging Ludo statistics: Game Completion duration, remaining coins of loser");    
    }
}

abstract class Game{
    // Add game related attributes and methods here
    public Game(){
    
    }
    public void getNextMove(){};
    public void makeNextMove(){}
    public abstract String getName();
}
class Chess extends Game implements Visitable{
    public String getName(){
        return Chess.class.getName();
    }
    public void accept(Visitor visitor){
        visitor.logGameStatistics(this);
    }
}
class Checkers extends Game implements Visitable{
    public String getName(){
        return Checkers.class.getName();
    }
    public void accept(Visitor visitor){
        visitor.logGameStatistics(this);
    }
}
class Ludo extends Game implements Visitable{
    public String getName(){
        return Ludo.class.getName();
    }
    public void accept(Visitor visitor){
        visitor.logGameStatistics(this);
    }
}

public class VisitorPattern{
    public static void main(String args[]){
        Visitor visitor = new GameVisitor();
        Visitable games[] = { new Chess(),new Checkers(), new Ludo()};
        for (Visitable v : games){
            v.accept(visitor);
        }
    }
}

Explanation:

  1. Visitable (Element) is an interface and this interface method has to be added to a set of classes.
  2. Visitor is an interface, which contains methods to perform an operation on Visitable elements.
  3. GameVisitor is a class, which implements Visitor interface ( ConcreteVisitor).
  4. Each Visitable element accept Visitor and invoke a relevant method of Visitor interface.
  5. You can treat Game as Element and concrete games like Chess,Checkers and Ludo as ConcreteElements.

In above example, Chess, Checkers and Ludo are three different games ( and Visitable classes). On one fine day, I have encountered with a scenario to log statistics of each game. So without modifying individual class to implement statistics functionality, you can centralise that responsibility in GameVisitor class, which does the trick for you without modifying the structure of each game.

output:

Logging Chess statistics: Game Completion duration, number of moves etc..
Logging Checkers statistics: Game Completion duration, remaining coins of loser
Logging Ludo statistics: Game Completion duration, remaining coins of loser

Use cases/Applicability:

  1. Similar operations have to be performed on objects of different types grouped in a structure
  2. You need to execute many distinct and unrelated operations. It separates Operation from objects Structure
  3. New operations have to be added without change in object structure
  4. Gather related operations into a single class rather than force you to change or derive classes
  5. Add functions to class libraries for which you either do not have the source or cannot change the source

Additional references:

oodesign

sourcemaking