通过访问者评估树表达

时间:2013-01-22 23:11:26

标签: java oop design-patterns visitor

我有一个复合设计模式的表达式:

interface TreeExpression{
    void accept(Visitor visitor);
}

abstract class Operator{
    TreeExpression childA;
    TreeExpression childB;

    Operator(TreeExpression a, TreeExpression b){
        this.childA = a;
        this.childB = b;
    }
}

class PlusTreeExpression extends Operator implements TreeExpression{
    public PlusTreeExpression(TreeExpression a, TreeExpression b) {
        super(a, b);
    }

    public void accept(Visitor visitor) {
        this.childA.accept(visitor);
        visitor.visit(this);
        this.childB.accept(visitor);
    }
}

class MultiplyTreeExpression extends Operator implements TreeExpression{
    public MultiplyTreeExpression(TreeExpression a, TreeExpression b) {
        super(a, b);
    }

    public void accept(Visitor visitor) {
        this.childA.accept(visitor);
        visitor.visit(this);
        this.childB.accept(visitor);
    }
}

class IntegerNode implements TreeExpression{
    Integer value;

    IntegerNode(int v){
        this.value = v;
    }

    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

和访问者从表达式获取字符串:

interface Visitor{
    void visit(PlusTreeExpression tree);
    void visit(MultiplyTreeExpression tree);
    void visit(IntegerNode node);
}

class PrintVisitor implements Visitor{
public StringBuffer result = new StringBuffer();


    public void visit(IntegerNode node) {
        result.append(node.value);
    }

    public void visit(PlusTreeExpression tree) {
        result.append("+");
    }

    public void visit(MultiplyTreeExpression tree) {
        result.append("*");
    }

这次访问是有效的,现在我正在尝试为访问者进行评估表达,但在这里我遇到了问题。我尝试了几种方法,但我不知道如何在不改变现有代码的情况下从子树获取价值。

2 个答案:

答案 0 :(得分:6)

据我所知,这里的问题是你要定义如何在树本身而不是访问者中遍历树。虽然这可能是一种有效的方法(设计模式确实有变化),但我认为在这种情况下,最好将树结构与遍历顺序(预订,有序,后顺序)分离。事实上,在教授这种模式时,一个典型的练习是写三个访问者,每个访问者执行不同的遍历。

在你的情况下,我会:

  1. 将表达式表示为树,就像您一样,但删除表单接受遍历部分。在您的代码中,接受看起来像:

    public void accept(Visitor visitor)
    {
        visitor.visit(this);
    }
    
  2. 为您的节点的子节点定义公共getter,以便访问者可以访问它们(getChildA()getChildB()getValue())。

  3. 根据您需要的遍历类型编写访问者。为了评估表达式,您通常会使用下订单,同时打印您可以按顺序使用的表达式(如您的示例所示)。因此,为了评估表达式,您将看到如下内容:

    class EvalVisitor implements Visitor{
    
            public Integer visit(IntegerNode node) {
                return node.getValue();
            }
    
            public Integer visit(PlusTreeExpression tree) {
                return this.visit(tree.getChildA()) + this.visit(tree.getChildB());
            }
    
            public Integer visit(MultiplyTreeExpression tree) {
                return this.visit(tree.getChildA()) * this.visit(tree.getChildB());
            }
        }
    
  4. HTH

答案 1 :(得分:0)

夫妻提示:

  1. 对于您的问题:考虑一个名为“堆栈”的数据结构(在Java中,java.util.LinkedList很有用),并查看名为“Reverse Polish Notation”的内容。

    < / LI>
  2. 顺便说一句,您可能希望简化您的方法。请注意,此代码一遍又一遍地重复:

    public void accept(Visitor visitor) {
        this.childA.accept(visitor);
        visitor.visit(this);
        this.childB.accept(visitor);
    }
    
  3. 随着你添加更多案件,情况会变得更糟。您可能需要考虑创建一个名为(例如)BinaryExpression的抽象类,其中包含部分或全部代码。

    你也可以考虑使用某种整数标记来识别特定的操作,而不是类的名称和“accept()”函数......然后在“访问者”中使用switch / case语句visit(BinaryExpression)的实施。