这里有一个用Java实现的访问者模式,用于计算像(1 + 2) + 3这样的表达式。这里的代码来自于以下代码示例:pattern#Sources。
interface Node
{
public int accept(Visitor v);
}
class ConstantNode implements Node
{
public int constant;
public ConstantNode(int constant)
{
this.constant = constant;
}
public int accept(Visitor v) {
return v.visit(this);
}
}
class SumNode implements Node
{
public Node left;
public Node right;
public SumNode(Node left, Node right)
{
this.left = left;
this.right = right;
}
public int accept(Visitor v) {
return v.visit(this);
}
}
interface Visitor
{
public int visit(ConstantNode n);
public int visit(SumNode n);
}
class EvalVisitor implements Visitor
{
public int visit(ConstantNode n) {
return n.constant;
}
public int visit(SumNode n) {
return n.left.accept(this) + n.right.accept(this);
}
}
public class VisitorDemo
{
public static void main(String[] args)
{
// First make an expression tree to represent the following.
//
// +
// / \
// + 3
// / \
// 1 2
Node a = new ConstantNode(1);
Node b = new ConstantNode(2);
Node c = new ConstantNode(3);
Node d = new SumNode(a, b);
Node e = new SumNode(d, c);
Visitor visitor = new EvalVisitor();
int result = e.accept(visitor);
System.out.println(result);
}
}我理解在每个递归级别上,要调用哪个visit()方法取决于访问者的类型(在本例中是evalVisitor)以及节点的类型(ConstantNode或SumNode),因此需要双重调度。但是,这种使用accept()和visit()方法实现双重调度的代码对我来说太费解了。但是,我看到的几乎所有访问者模式示例都使用这种通过accept()将访问者传递到节点的方法,后者反过来调用访问者的visit()方法来执行双重分派。
为什么代码示例不能像这样简单呢?
interface Node
{
}
class ConstantNode implements Node
{
public int constant;
public ConstantNode(int constant)
{
this.constant = constant;
}
}
class SumNode implements Node
{
public Node left;
public Node right;
public SumNode(Node left, Node right)
{
this.left = left;
this.right = right;
}
}
interface Visitor
{
public int visit(Node n) throws Exception;
}
class EvalVisitor implements Visitor
{
public int visit(Node n) throws Exception {
if (n instanceof ConstantNode) {
return ((ConstantNode) n).constant;
} else if (n instanceof SumNode) {
return this.visit(((SumNode) n).left) + this.visit(((SumNode) n).right);
} else {
throw new Exception("Unsupported node");
}
}
}
public class SimpleVisitorDemo
{
public static void main(String[] args) throws Exception
{
// First make an expression tree to represent the following.
//
// +
// / \
// + 3
// / \
// 1 2
Node a = new ConstantNode(1);
Node b = new ConstantNode(2);
Node c = new ConstantNode(3);
Node d = new SumNode(a, b);
Node e = new SumNode(d, c);
Visitor visitor = new EvalVisitor();
int result = visitor.visit(e);
System.out.println(result);
}
}在这个代码示例中,我完全消除了在每个节点中实现apply()的需要,访问的全部逻辑--包括双重分派的逻辑--现在只包含在访问者类中。
我有以下问题:
您能客观地列举简化的访问者模式在代码的可维护性或效率方面的问题吗?
发布于 2015-06-26 17:40:59
为什么代码示例不能更简单..。
因为您的示例将虚拟调度替换为switch分派(您在对象子类型上实现了一个if链)。这种方法很难维护,因为您在检测继承层次结构的更改时没有得到编译器的帮助。
简化实现的具体问题是在最后一个else中,您返回零。一个更常见的解决方案是在那里抛出一个异常,因为您真的不知道您有什么样的节点。
现在,想象一下用一个SubtractNode扩展层次结构。这自然需要在Visitor接口中添加一个方法,确保所有访问者都必须在编译时处理新的节点子类型。
另一方面,简化的示例将继续编译,在您的示例中,它也将继续运行,返回SubtractNode的错误结果。
https://stackoverflow.com/questions/31078995
复制相似问题