This week we’ll look at the last behavioural design pattern, the Visitor pattern.
The Gang of Four book defines the Visitor pattern as:
“Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.”
The Visitor pattern allows us to separate algorithms from the objects on which they operate.
Explanation
We usually extend the behaviour of a class hierarchy by adding additional methods for the behaviour we need.
Occasionally the additional behaviours are not consistent with the design of the existing class model. Depending on how we have designed the class hierarchy, it may be impossible to extend the behaviours without modifying many or all of the classes. Other times we don’t have enough information about the future required behaviours to accommodate them in our design.
By using the Visitor pattern, we can support the possibility of adding behaviours of the class hierarchy at some time in the future. By separating the algorithm from the data it works on, the pattern allows us to define new operations without changing the classes in the hierarchy.
The Visitor pattern implements double dispatch. Usually when we invoke a method, we use single dispatch. The method name and the target object type determines which method gets invoked. In double dispatch, the method that gets invoked depends on the method name and the types of two objects: the type of the visitor and the type of the element it visits.
Double dispatch in the Visitor pattern works by having two different abstract/interface types each having a method that takes a parameter of the other type. One type calls the method of the other type, and that method executes the desired strategy on the first type.
Participants
The participants of the Visitor pattern are the following:
Element
: an interface that contains anaccept()
method that takes aVisitor
as an argument.ConcreteElement
: implements theaccept()
method defined inElement
.Visitor
: an interface that declares avisit()
method for each class ofConcreteElement
in the object structure.ConcreteVisitor
: the concrete classes that appropriately implement each method declared by theVisitor
interface.
We follow these steps to implement the Visitor pattern:
- We add an
accept(Visitor)
method to theElement
hierarchy. - We create a
Visitor
base class/interface with avisit()
method for every element type. - We create concrete derived classes, appropriately implementing each method in the
Visitor
base class/interface . - The client code creates visitor objects and passes each object to the
accept()
calls.
Simple Implementation Code
Let’s first create the Element
interface with an accept()
method that takes a Visitor
as an argument:
public interface Element { void accept(Visitor v); }
Next we implement three concrete classes implementing the Element
interface. Here we’ll use class names from the Hickory, Dickory, Dock limerick. Only the first class is shown; the others are identical except in name.
public class Hickory implements Element { public void accept(Visitor v) { v.visit(this); } @Override public String toString() { return "Hickory"; } }
Next we define the Visitor
interface:
interface Visitor { void visit(Hickory hickory); void visit(Dickory dickory); void visit(Dock dock); }
Now let’s create two concrete implementations of the Visitor
interface, UpVisitor
and DownVisitor
. Again only one is shown; the other is identical except for running down the clock.
class UpVisitor implements Visitor { public void visit(Hickory hickory) { System.out.println(hickory + " runs up the clock."); } public void visit(Dickory dickory) { System.out.println(dickory + " runs up the clock."); } public void visit(Dock dock) { System.out.println(dock + " runs up the clock."); } }
Finally we create the client code that creates visitor objects and passes each object to the accept()
methods.
public class VisitorClient { public static void main(String args[]) { Element list[] = {new Hickory(), new Dickory(), new Dock()}; UpVisitor up = new UpVisitor(); DownVisitor down = new DownVisitor(); for (Element element : list) element.accept(up); for (Element element : list) element.accept(down); } }
Design Example
We’ll continue the Visitor pattern next week where we’ll look at a longer example from our first person shooter game.
Stay tuned!
As always, please share your comments and questions.