The Visitor pattern lets us define a new operation on an object structure without changing the classes of the objects on which it operates. Rather than writing dedicated methods for each programming task and afterwards recompiling, the idea is to (1) insert a so-called accept method in each class, and (2) write the action code in so-called visitors.
For our running example, the basic classes and the visitor class look like this:
interface List {
void accept(Visitor v);
}
class Nil implements List {
public void accept(Visitor v) {
v.visitNil(this);
}
}
class Cons implements List {
int head;
List tail;
public void accept(Visitor v) {
v.visitCons(this);
}
}
interface Visitor {
void visitNil(Nil x);
void visitCons(Cons x);
}
class SumVisitor implements Visitor {
int sum = 0;
public void visitNil(Nil x) {}
public void visitCons(Cons x) {
sum = sum + x.head;
x.tail.accept(this);
}
}
Each accept method takes a visitor as argument.
The interface Visitor has a header for each of the basic classes.
We can now compute and print the sum of all components of a given
List-object l
by writing
SumVisitor sv = new SumVisitor();
l.accept(sv);
System.out.println(sv.sum);
The advantage is that one can write code that manipulates objects of existing classes without recompiling those classes. The price is that all objects must have an accept method.
In summary, the Visitor pattern combines the advantages of the two other approaches. In slogan-form:
Frequent | Frequent | |
type casts? | recompilation? | |
Instanceof and type casts | Yes | No |
Dedicated methods | No | Yes |
The Visitor pattern | No | No |