2

我使用访问者模式在类的层次结构上执行函数。例如:

// Node.java

public abstract class Node {}

// Addition.java

public final class Addition extends Node {
    public final Node e1;
    public final Node e2;

    public Addition(final Node e1, final Node e2) {
        this.e1 = e1;
        this.e2 = e2;
    }
}

// Multiplication.java

public final class Multiplication extends Node {
    public final Node e1;
    public final Node e2;

    public Multiplication(final Node e1, final Node e2) {
        this.e1 = e1;
        this.e2 = e2;
    }
}

// Constant.java

public final class Constant extends Node {
    public final int value;

    public Constant(final int value) {
        this.value = value;
    }
}

因为我有不同的函数来获取一个节点并返回不同的类型,所以我为我的访问者定义了一个接口:

public interface INodeVisitor<T> {
    public abstract T visit(final Addition add);
    public abstract T visit(final Multiplication add);
    public abstract T visit(final Constant add);

    public default T visit(final Node node) {
        throw new RuntimeException("Unknown node: " + node.getClass() + ".");
    }
}

然后我可以为我的所有节点定义一个通用方法accept(),它允许我定义新访问者而无需修改节点:

// I add this in the definition of every node.
public <T> T accept(final INodeVisitor<T> visitor) {
    return visitor.visit(this);
}

我现在可以在节点上定义函数,例如:

// This visitor is a function on Nodes and returns an Integer.
public final class NodeEvaluationVisitor implements INodeVisitor<Integer> {
    public final Integer visit(final Addition add) {
        return add.e1.accept(this) + add.e2.accept(this);
    }

    public final Integer visit(final Multiplication mul) {
        return mul.e1.accept(this) * mul.e2.accept(this);
    }

    public final Integer visit(final Constant c) {
        return c.value;
    }
}

这完美地工作:

public class Main {
    public static Integer eval(final Node n) {
        return n.accept(new NodeEvaluationVisitor());
    }

    public static void main(final String[] args) {
        final Node n = new Addition(new Multiplication(new Constant(5), new Constant(8)), new Constant(2));
        System.out.println(eval(n));
    }
}

输出:

42

这是有效的,因为我的界面可以访问所有层次结构。我的问题是什么时候Node定义的projectA,它的子类定义在projectB依赖于projectA.

由于Node还需要定义一个accept()接受INodeVisitoras 参数的方法,所以我在projectA. 然后我可以在projectB.

但是,这意味着我的接口需要对 的子类是通用的Node,这似乎是不可能的。使用此界面:

public interface INodeVisitor<T> {
    public default T visit(final Node node) {
        throw new RuntimeException("Unknown node: " + node.getClass() + ".");
    }
}

我得到:

Exception in thread "main" java.lang.RuntimeException: Unknown node: class Addition.
at INodeVisitor.visit(INodeVisitor.java:5)
at Addition.accept(Addition.java:14)
at Main.eval(Main.java:9)
at Main.main(Main.java:14)

由于Java的动态调度选择了default方法。与以下结果相同:

public interface INodeVisitor<T> {
    public default <U extends Node> T visit(final U node) {
        throw new RuntimeException("Unknown node: " + node.getClass() + ".");
    }
}

我发现完成这项工作的唯一方法是手动进行调度:

public interface INodeVisitor<T> {
    public abstract T visit(final Node node);
}

所以在NodeEvaluationVisitor

@Override
public final Integer visit(final Node node) {
    if (node instanceof Addition)
        return visit((Addition) node);
    if (node instanceof Multiplication)
        return visit((Multiplication) node);
    if (node instanceof Constant)
        return visit((Constant) node);
    throw new RuntimeException("Unknown node: " + node.getClass() + ".");
}

问题: Java中是否有比上面不理想的手动调度解决方案更好的解决方案?感谢您的阅读和您的建议。

编辑:我最终实现的解决方案(仍然不满意)是添加一个特定的接口,例如INodeSpecificVisitor<T>projectB让访问者实现这个特定的接口,并在INodeSpecificVisitor. 这样,至少手动调度在访问者之间进行了分解。

4

0 回答 0