MrUll's

Juli 26, 2004

using "double dispatch" technique to implement a visitor pattern

visitor gof pattern is easy to unterstand when you know how "double dispatch" works. To refresh your mind, use it when you have to implement some operations on the elements of a object structure. an example is to generate a special XML representation, maybe representing a personalized view. the elements of the structure rarely change but its to be expected that some new operations are to be coded sooner or later. you like to keep the objectstructure separated from code to do a desired operation, means you dont want to implement operation-specific methods in your objectstructure classes. with this an an objectstructure which will rarely change things go like this:
for each desired operation a "visitor" has to be implemented, e.g. three operations over a structure of objects means that there a three implementations of the visitor interface. for each element in the structure a visitor implements a callback method. all elements offer an entrypoint for a visitor. with this, a client instantiates a visitor, iterates over the elements, and passes the visitor to the element. The element then calls his callback method on the passed visitor. this means the element passes himself as actual parameter. look at the following example. (structure contains only two elements)

public interface Visitor {
public void visitConcreteElement(Element1 element1);
public void visitConcreteElement(Element2 element2);
}

public class ConcreteVisitor implements Visitor {
public void visitConcreteElement(Element1 elem) {
elem.operationA();
}
public void visitConcreteElement(Element2 elem) {
elem.operationB();
}
}

public interface Element {
public void accept(Visitor v);
}

// Element2 similar
public class Element1 implements Element {
public void accept(Visitor v) {
v.visitConcreteElement(this);
}
public void operationA() {
System.out.println("Element1 :: Element1.operationA() executing");
}


processing over structure then goes like this:

visitor = new ConcreteVisitor();
for (Iterator iter = objectStruct.iterator(); iter.hasNext();) {
((Element) iter.next()).accept(visitor);
}

Note that this separates processing from the data structures. As processing comes from outside (the visitor), the elements have to publish some additional methods, but operation-scoped methods are drawn to the visitor.