我使用访问者模式在类的层次结构上执行函数。例如:
// 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()
接受INodeVisitor
as 参数的方法,所以我在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
. 这样,至少手动调度在访问者之间进行了分解。