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.
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.
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.