这是一个用Java实现的访问者模式,用于评估像(1 + 2)+ 3这样的表达式。这里的代码的灵感来自于https://en.wikipedia.org/wiki/Visitor_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
)以及节点的类型({{ 1}}或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);
}
}
的需要,并且访问的总逻辑(包括双重调度的逻辑)现在仅包含在访问者类中。
我有以下问题:
您可以根据可维护性或代码效率客观地列举简化访客模式的问题吗?
答案 0 :(得分:3)
为什么代码示例不能更简单,[...]
因为您的示例使用switch
调度替换了虚拟调度(您在对象子类型上将其实现为if
s链)。这种方法非常难以维护,因为在检测对继承层次结构的更改时,编译器没有得到任何帮助。
简化实现的具体问题在于最后else
,其中您返回零。一个更常见的解决方案是在那里抛出异常,因为你真的不知道你有什么样的节点。
现在想象一下用SubtractNode
扩展层次结构。这自然需要在Visitor
接口中添加一个方法,确保所有访问者都被迫在编译时处理新的节点子类型。
简化示例将继续编译,在您的情况下,它也将继续运行,为SubtractNode
返回错误的结果。