You've identified the following as reasons why you can't introduce new operations with a visitor:
- "When you pass an visitor to an element, it will traverse the entire substructure of that object."
- "Further you can't return anything from the accept/visit methods."
I think that reason (1) is often a valid concern. However, I think that you can modify your visitor to get around this. Suppose that you make visit
and accept
return a boolean
indicating whether the exploration of the structure should continue. You could then have accept
work like this (in pseudocode):
for (each child) {
if (!child.accept(visitor)) return false;
}
return visitor.visit(this);
That way, as soon as the visitor recognizes that it's done, it can stop searching. This is similar to doing a depth-first, backtracking search of the objects linked together.
For (2), however, I think you're missing an important technique with visitors - since visitors are objects, they can contain private state information. For example, suppose that I want to write a simple count
function that returns the total number of elements in the structure. Then I could write something like this:
public int count(Base object) {
Visitor v = new Visitor() {
private int numElems = 0;
public void visit(Base object) {
numElems++;
}
public void visit(Derived1 object) {
numElems++;
}
// ... etc
public int getCount() {
return numElems;
}
};
object.accept(v);
return v.getCount();
}
Notice that even though visit
and accept
return void
here, I can still build an overall method that returns a value by having the visitor maintain internal state and then returning that state once the visiting is completed. From now on, calling count(obj)
wil; return a count of all objects, essentially "grafting" on an extra method to the class hierarchy.
Hope this helps!