Search code examples
javadesign-patternsvisitor-patterndynamic-binding

Java - Multiple visitor patterns for the same class structure


So I have a class structure of expressions, composed by, Binary Expressions, Unary Expressions, all abstract, these expanding into their specific concrete classes of operations such as Add, Sub, Mul, Not, etc... Like such.

Class Structure

I want to create 2 visitors for each of these classes. One for toString and one to evaluate the expression (calculate the value).

The problem is, my toStringVisitor should always return a String, but the evaluateVisitor will return variable types depending on the operation (ie: Add will return an int, Lower than or Not will return a boolean, some others will return other types as well)

Can I avoid creating 2 visitor interfaces for these 2 visitors?

For example what I have right now

public interface Visitor {

Public int visit(Neg c);
Public int visit(Add c);
Public int visit(Sub c);
Public int visit(Mul c);
Public boolean visit(Lowerthan c);
Public boolean visit(Greaterthan c);
Public boolean visit(Equal c);
Public boolean visit(Not c);
Public boolean visit(And c);
Public boolean visit(Or c);

}

Will only work for the evaluateVisitor and not for the toString visitor.

Also, what is the difference between using for example:

Public int visit(Neg c);
Public int visit(Add c);
Public int visit(Sub c);
Public int visit(Mul c);

and

Public int visitNeg(Neg c);
Public int visitAdd(Add c);
Public int visitSub(Sub c);
Public int visitMul(Mul c);

I was told I should differentiate the visit methods to avoid dynamic binding issues but I can't really figure out why that would be the case.


Solution

  • You can make the visitor generic:

    interface Visitor<R> {
        R visit(Neg c);
        ...
    }
    

    So your ToStringVisitor will implement Visitor<String>, and the other two will implement Visitor<Integer> and Visitor<Boolean>.

    Regarding using visit() vs. visitNeg(), it's a matter of choice. Some feel that using distinct names clutters the API. Some feel using overloading make things more complex to understand, and bug-prone. I'm personally not a big fan of overloading.