Visitor pattern
The visitor pattern is a way of separating an algorithm from an object structure on which it operates.
A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures. It is one way to easily follow the open/closed principle. In essence, the visitor allows one to add new virtual functions to a family of classes without modifying the classes themselves; instead, one creates a visitor class that implements all of the appropriate specializations of the virtual function. The visitor takes the instance reference as input, and implements the goal through double dispatch.
Example :
Below example shows how the contents of a tree of nodes (below case describing the components of a car) can be printed. Instead of creating “print” methods for each subclass (Wheel, Engine, Body, and Car), a single class (CarElementPrintVisitor) performs the required printing action. Because different subclasses require slightly different actions to print properly, CarElementDoVisitor dispatches actions based on the class of the argument passed to it.
public interface CarElementVisitor { void visit(Wheel wheel); void visit(Engine engine); void visit(Body body); void visit(Car car); }
public interface CarElement { void accept(CarElementVisitor visitor); // CarElements have to provide accept(). }
public class Wheel implements CarElement { private String name; public Wheel(String name) { this.name = name; } public String getName() { return this.name; } public void accept(CarElementVisitor visitor) { /* * accept(CarElementVisitor) in Wheel implements * accept(CarElementVisitor) in CarElement, so the call to accept is * bound at run time. This can be considered the first dispatch. * However, the decision to call visit(Wheel) (as opposed to * visit(Engine) etc.) can be made during compile time since 'this' is * known at compile time to be a Wheel. Moreover, each implementation of * CarElementVisitor implements the visit(Wheel), which is another * decision that is made at run time. This can be considered the second * dispatch. */ visitor.visit(this); } }
public class Engine implements CarElement { public void accept(CarElementVisitor visitor) { visitor.visit(this); } }
public class Body implements CarElement { public void accept(CarElementVisitor visitor) { visitor.visit(this); } }
public class Car implements CarElement { CarElement[] elements; public Car() { // create new Array of elements this.elements = new CarElement[] { new Wheel("front left"), new Wheel("front right"), new Wheel("back left"), new Wheel("back right"), new Body(), new Engine() }; } public void accept(CarElementVisitor visitor) { for (CarElement elem : elements) { elem.accept(visitor); } visitor.visit(this); } }
public class CarElementPrintVisitor implements CarElementVisitor { public void visit(Wheel wheel) { System.out.println("Visiting " + wheel.getName() + " wheel"); } public void visit(Engine engine) { System.out.println("Visiting engine"); } public void visit(Body body) { System.out.println("Visiting body"); } public void visit(Car car) { System.out.println("Visiting car"); } }
public class CarElementDoVisitor implements CarElementVisitor { public void visit(Wheel wheel) { System.out.println("Kicking my " + wheel.getName() + " wheel"); } public void visit(Engine engine) { System.out.println("Starting my engine"); } public void visit(Body body) { System.out.println("Moving my body"); } public void visit(Car car) { System.out.println("Starting my car"); } }
public class VisitorDemo { public static void main(String[] args) { Car car = new Car(); car.accept(new CarElementPrintVisitor()); car.accept(new CarElementDoVisitor()); } }