Java – Visitor stability vs instanceof flexibility

design-patternsjavaobject-oriented-design

I am working on a GUI application which generates a configuration file. I have a class hierarchy for the configuration model and I use an object tree of that hierarchy in several different contexts. Currently, I use the Visitor pattern to avoid polluting my model classes with context specific code.

interface IConfigurationElement {
    void acceptVisitor(IConfigurationElementVisitor visitor);
}

In an earlier version I used chains of instanceof conditions instead of the Visitor. Comparing the two approaches I see the following tradeofs.

Visitor

  • It is easier and safer to add new IConfigurationElement. Just add a new declaration to IConfigurationElementVisitor and the compiler generates errors for all visitor implementations. With instanceof chains you have to remember all the places you have to extend with the new configuration element. Basically instanceof violates the DRY principle as it duplicates logic in several places.
  • The visitor pattern is more efficient than a chain of instanceofconditions

instanceof

  • The great advantage of instanceof is its flexibility. For example instanceof allows me to define special solutions for different subsets of IConfigurationElement implementations which need to be handled similarilly in some cases. In contrast, Visitor forces me to implement a method for each implementation class every time.

Is there a common solution for this kind of problem? Can I adapt the Visitor somehow, so I can provide a common solution for some cases?

Best Answer

You could use visitor with instanceOf

interfaces:

interface Visitable {
  void accept(Object visitor);
}
interface AVisitor {
  void visitA(A a);
}
interface BVisitor {
  void visitB(B b);
}
interface CVisitor {
  void visitB(C c);
}

Visitables:

class C implements Visitable {
  public void accept(Object visitor) {
    if (visitor instanceof CVisitor) {
      ((BVisitor)vistor).visitC(this);
    }
  }
}

class B implements Visitable {
  public void accept(Object visitor) {
    if (visitor instanceof BVisitor) {
      ((BVisitor)vistor).visitB(this);
    }
  }
}

class A extends B implements Visitable {
  public void accept(Object visitor) {
    super.accept(visitor);
    if (visitor instanceof AVisitor) {
      ((AVisitor)vistor).visitA(this);
    }
  }
}

Visitors:

class PrintBs implements BVisitor {
  public void visitB(B b) {
    system.out.println(b);
  }
}

class PrintAs implements AVisitor {
  public void visitA(A a) {
    system.out.println(a);
  }
}

class PrintCs implements CVisitor {
  public void visitC(C c) {
    system.out.println(c);
  }
}
class PrintAsAndCs implements CVisitor, AVisitor{
  public void visitA(A a) {
    system.out.println(a);
  }
  public void visitC(C c) {
    system.out.println(c);
  }
}

each class knows only about its related interfaces, so adding new vistors nor visitables requires changing everything in that category (visitor/visitable) (for visitor, it does not require changing anything, for visitable it requires creating new visitor interface, but again, no changing of existing objects).

This way, there is no chain of instanceof tests and visitor for subset does not need to even know about types outside this subset.

Question is what to do with situation where A extends B (and B is Visitable too), in that case, you could just add super.accept(visitor) into accept (thus it would be short chain of instanceof-s, but onlu as long as hierarchy is deep, and it should not be too deep to matter, abd you don't need to write it whole manually).

Related Topic