Mark Mark - 25 days ago 10
Java Question

Visitor Pattern for Interpreter - Invoking method based on instance type

The below code does not compile due to incompatible types obviously. However, I am trying to do something similar. In order to get this to work to my knowledge, I would have to take the superclass and check what type it is and use a switch case or a nasty if else set. I can leverage polymorphism here and place the visit method in ASTNode and override it in each sublcass, however, that is putting logic in the ASTNode class when I want to make sure this remains in the Interpreter class.

public class Test {

private class ASTNode{

}
private class ExprNode extends ASTNode{

}
private class VarNode extends ASTNode{

}
private class Interpreter{

public Interpreter(ASTNode node){
this.visit(node);
}

public void visit(ExprNode node){

}
public void visit(VarNode node){

}

}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {

ASTNode node = new ExprNode();
Interpreter interpreter = new Interpreter(node);
}
}


I am building an interpreter and I am using the Visitor Pattern for the Interpreter. There is a visit method for each type of node. In the example I am working from which was written in python, they use a getattr method that dispatched the call to the right method based on the subclass. I do not know of anything similar in Java.

Lastly, if I were to use polymorphism here I would be defining different visit methods for the Interpreter and Symantic Analyzer in the ASTNode classes. I like the idea of have Interpreter logic in the interpreter class but I would hate to have a huge switch case based on the subclass type.

Any ideas here? Maybe I am missing an easy technique.

Answer

The point of the visitor pattern is to replace the "huge switch" with polymorphism by handling the visitor invocation on the individual subclass. It doesn't mean that the logic is contained in the subclasses, rather that's defined on the visitor class. Here's how that would look in your example:

interface NodeVisitor {
    void visit(ExprNode node);
    void visit(VarNode node);
}

class Interpreter implements NodeVisitor {
    @Override
    public void visit(ExprNode node) {
        // custom logic here
    }

    @Override
    public void visit(VarNode node) {
        // custom logic here
    }
}

private class ASTNode {
    public abstract void accept(NodeVisitor visitor);
}

private class ExprNode extends ASTNode {
    @Override
    public void accept(NodeVisitor visitor) {
        visitor.visit(this);
    }
}

private class VarNode extends ASTNode {
    @Override
    public void accept(NodeVisitor visitor) {
        visitor.visit(this);
    }
}

public static void main(String[] args) {
    ASTNode node = new ExprNode();
    node.accept(new Interpreter());
}