I'm looking for a clean design to emulate Visitor functionality without the many drawbacks it has. In Java, the traditional implementations (as the described in GoF) resort to double dispatch to get rid of if-elses. To solve this, I've seen some implementations that use reflection to avoid modifications on the "Visitable" classes, but these rely on hardcoded strings when looking for method names. Although quite useful, I still think that they are not clean design.
Is it possible to emulate the same idea using data structures and/or good OO-design? It doesn't have to be a pattern, just I'm looking for examples where a similar problem is solved (e.g.: using a Map<Class<T>,SomeFunctionObject>
).
UPDATE Something like this:
public abstract class BaseVisitor<T> {
private final TypesafeHeterogeneusMap map;
protected BaseVisitor(){
map = inflateFunctions();
}
public <E extends T> void process(E element){
if(element == null){
throw new NullPointerException();
}
boolean processed = false;
@SuppressWarnings("unchecked")
Class<? super T> sc = (Class<? super T>) element.getClass();
while(true){
if(sc != null){
FunctionObject<? super T> fo2 = map.get(sc);
if(fo2 != null){
fo2.process(element);
processed = true;
break;
}
sc = sc.getSuperclass();
} else {
break;
}
}
if(!processed) System.out.println("Unknown type: " + element.getClass().getName());
}
abstract TypesafeHeterogeneusMap inflateFunctions();
}
Actually is a mix of Template pattern and Command pattern, I think. Feel free to post your suggestions on how to enhance it.
You could just make all your Visitor implementations extend a base class, which provides a default implementation for every type of Visitable:
Then, when a new class
Cat
is introduced, you add thevisitCat(Cat cat)
method to the interface and the base class, and all the visitors are left unchanged and still compile. If they don't want to ignore cats, then you override thevisitCat
method.