How to Solve Cyclic Dependencies in a Visitor Pattern

dependenciesdesign-patternsjava

When programming at work we now and then face a problem with visitors and module/project dependencies.

Say you have a class A in a module X. And there are subclasses B and C in module Y. That means that module Y is dependent on module X. If we want to implement a visitor pattern to the class hierarchy, thus introducing an interface with the handle Operations and an abstract accept method in A, we get a dependency from module Y to module X, which we cannot allow for architectural reasons.

What we do is, use a direct comparison of the types (i.e. instanceof, since we program in Java), which is not satisfying.

My question(s) would be: Do you encounter this kind of problem in your daily work (or do we make poor architectural choices) and if so, how is your approach to solve this?

Here is a minimal example in Java to illustrate my point. Package a has ClassA and the Visitor-Interface over the ClassA Hierarchy:

package pkg.a;  
public abstract ClassA extends ClassA {  
    public abstract void accept(ClassAVisitor visitor);  
   /* other methods ... */   
} 

package pkg.a;  
import pkg.b.ClassB;
import pkg.b.ClassC;
public interface ClassAVisitor {  
    public abstract void handle(ClassB visitee);
    public abstract void handle(ClassC visitee);  
} 

Package b has the concrete classes that extend from ClassA:

package pkg.b;  
import pkg.a.ClassAVisitor;
public ClassB extends ClassA {  
    public void accept(ClassAVisitor visitor) {  
        visitor.handle(this);  
    }   
} 

package pkg.b;
import pkg.a.ClassAVisitor;  
public ClassC {  
    public void accept(ClassAVisitor visitor) {  
        visitor.handle(this);  
    }   
}

Package a and b have a cyclic dependency.

Best Answer

What about changing the visitor interface to remove the dependency from a to b?

package pkg.a;  
public abstract ClassA extends ClassA {  
    public abstract void accept(ClassAVisitor visitor);  
   /* other methods ... */   
} 

package pkg.a;  
public interface ClassAVisitor {  
    public abstract void handleB(ClassA visitee);
    public abstract void handleC(ClassA visitee);  
} 

and then

package pkg.b;
import pkg.a.ClassAVisitor;
public ClassB extends ClassA {
    public void accept(ClassAVisitor visitor) {
        visitor.handleB(this);
    }
}

package pkg.b;

import pkg.a.ClassAVisitor;
public ClassC {
    public void accept(ClassAVisitor visitor) {
        visitor.handleC(this);
    }
}