Learning Design Patterns – Visitor Pattern

Visitor pattern is a behavioral object design pattern (Behavioral design patterns are design patterns that identify common communication patterns between objects). It provides 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 follow the open/closed principle and avoids the breach of Single Responsibility Principle, as adding an external functionality which could change for different reasons, to an object structure violates the Single Responsibility 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. Double Dispatch mechanism is used to implement this facility. Double dispatch is a special mechanism that dispatches a function call to different concrete functions depending on the runtime types of two objects involved in the call. For languages supporting double dispatch, like Common Lisp, implementing the visitor design pattern is trivial.

Design Participants / Components

  • Visitor: This is an interface of an abstract class used to declare the visit operations for all the types of Visitable classes.
  • ConcreteVisitor: For each type of visitor all the visit methods, declared in abstract visitor, must be implemented. Each visitor will be responsible for different operations.
  • Visitable: Is an interface which declares the accept operation. This is the entry point which enables an object to be “visited” by the visitor object.
  • ConcreteVisitable: Those classes which implements the Visitable interface. And defines the accept operation. The visitor object is passed to this object using the accept operation.

515px-visitordiagram-svg
VisitorDiagram” by MattGiuca – This file was derived from:  VisitorClassDiagram.png. Licensed under Public Domain via Commons.

Now lets look at an implementation

Element/Visitable Interface:

package com.vai.design.visitor;

public interface CarElement {
	void accept(CarElementVisitor visitor);
}

Visitor Interface Interface:

package com.vai.design.visitor;

public interface CarElementVisitor {
	void visit(Wheel wheel);
	void visit(Engine engine);
	void visit(Body body);
	void visit(Car car);
}

ConcreteVisitable Classes:

package com.vai.design.visitor;

public class Wheel implements CarElement {
	private String name;

	public Wheel(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}
	
	@Override
	public void accept(CarElementVisitor visitor) {
		/**
		 * accept(CarElementVisitor) in Wheel implements
		 * accept(CarElementVisitor) in CarElement, so the call
		 * to accept is bound at runtime. 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);
	}

}
package com.vai.design.visitor;

public class Engine implements CarElement{
	@Override
	public void accept(CarElementVisitor visitor) {
		visitor.visit(this);
	}
}
package com.vai.design.visitor;

public class Body implements CarElement {
	@Override
	public void accept(CarElementVisitor visitor) {
		visitor.visit(this);
	}
}
package com.vai.design.visitor;

public class Car implements CarElement {
	private CarElement[] elements;

	public Car() {
		this.elements = new CarElement[] { new Wheel("front left"),
				new Wheel("front right"), new Wheel("back left"),
				new Wheel("back right"), new Body(), new Engine() };
	}

	@Override
	public void accept(CarElementVisitor visitor) {
		for (CarElement element : elements) {
			element.accept(visitor);
		}
		visitor.visit(this);
	}

}

Concrete Visitor Classes:

package com.vai.design.visitor;

public class CarElementPrintVisitor implements CarElementVisitor {

	@Override
	public void visit(Wheel wheel) {
		System.out.println("Visiting " + wheel.getName() + " wheel");
	}

	@Override
	public void visit(Engine engine) {
		System.out.println("Visiting Engine");

	}

	@Override
	public void visit(Body body) {
		System.out.println("Visiting body");
	}

	@Override
	public void visit(Car car) {
		System.out.println("Visiting car");
	}

}
package com.vai.design.visitor;

public class CarElementDoVisitor implements CarElementVisitor {

	@Override
	public void visit(Wheel wheel) {
		System.out.println("Kicking my " + wheel.getName() + " wheel");
	}

	@Override
	public void visit(Engine engine) {
		System.out.println("Starting my engine");
	}

	@Override
	public void visit(Body body) {
		System.out.println("Moving my body");
	}

	@Override
	public void visit(Car car) {
		System.out.println("Starting my car");

	}

}

Client:

package com.vai.design.visitor;

public class Client{
	public static void main(String[] args) {
		CarElement car = new Car();
		car.accept(new CarElementPrintVisitor());
		car.accept(new CarElementDoVisitor());
	}
}


Leave a comment