访客模式和逻辑运算

时间:2016-03-17 15:10:32

标签: c# design-patterns visitor

好吧,我一直在讨论我遇到的这个问题。

我创建了一个带有逻辑节点的树结构,例如而且,或者,等于,介于两者之间。 我不希望这些节点拥有除访问者接受方法之外的任何东西,因为会有更多访问者使用不同的实现。

所以例如我正在访问和。它首先访问自己所以我知道我正在处理一个,然后它正在访问左侧和右侧节点。这可以是任何早期的节点。

问题是,我需要一种方法来检查我是否已完成访问子节点。 例如,我想要这个输出

  

“和(equals()方法/或(equals()方法/()之间)”

但由于我无法知道孩子何时被访问过,所以会有这样的事情。

  

“和()等于()/或()等于()/()之间

有关如何克服此问题的任何建议?

2 个答案:

答案 0 :(得分:0)

因此,您需要在访问节点时跟踪您在树中的位置。为了能够做到这一点,你需要介绍一些上下文。

这样的事情:

public class VisitContext
{
    // Visited node
    public VisitedType Visited { get; set; }

    // Visited node is the left child node
    public bool IsLeftNode { get; set; }

   // Visited node is the right child node
    public bool IsRightNode { get; set; }
}

所以访客得到了这份合同:

public interface IVisitor
{
    public void Visit(VisitContext context);
}

现在你只需要遍历它。关键是要有一个受保护的重载,告诉孩子他们是哪种类型。请注意递归调用。

public class VisitedType
{
    public void Accept(IVisitor visitor)
    {
        var context = new VisitContext{ Visited = this };
        visitor.Visit(context);

        _leftNode.AcceptAsLeft(context);
        _rightNode.AcceptAsRight(context);
    }

    protected void AcceptAsLeft(VisitContext context)
    {
        context.IsLeftNode=true;
        context.IsRightNode=false;
        visitor.Visit(context);

        _leftNode.AcceptAsLeft(context);
        _rightNode.AcceptAsRight(context);
    }

    protected void AcceptAsRight(VisitContext context)
    {
        context.IsLeftNode=false;
        context.IsRightNode=true;
        visitor.Visit(context);

        _leftNode.AcceptAsLeft(context);
        _rightNode.AcceptAsRight(context);
    }
}

现在你知道什么时候你在树上左右走,但你仍然不知道你在树的哪个地方。要做到这一点,让我们介绍一个跟踪我们当前路径的血统/面包屑。

public class VisitContext
{
    public VisitedType Visited { get; set; }
    public bool IsLeftNode { get; set; }
    public bool IsRightNode { get; set; }

    // this.
    public LinkedList<VisitedType> Lineage { get; set; }       
}

并更新访问类型:

public class VisitedType
{
    public void Accept(IVisitor visitor)
    {
        var context = new VisitContext{ Visited = this, Lineage = new LinkedList<VisitedType>() };
        context.Lineage.AddLast(this);
        visitor.Visit(context);

        _leftNode.AcceptAsLeft(context);
        _rightNode.AcceptAsRight(context);
    }

    protected void AcceptAsLeft(VisitContext context)
    {
        //add a bread crumb
        context.Lineage.AddLast(this);
        context.IsLeftNode=true;
        context.IsRightNode=false;
        visitor.Visit(context);

        _leftNode.AcceptAsLeft(context);
        _rightNode.AcceptAsRight(context);

        //remove us when we've visited our children
        context.Lineage.RemoveLast();
    }

    protected void AcceptAsRight(VisitContext context)
    {
        //add a bread crumb
        context.Lineage.AddLast(this);
        context.IsLeftNode=false;
        context.IsRightNode=true;
        visitor.Visit(context);

        _leftNode.AcceptAsLeft(context);
        _rightNode.AcceptAsRight(context);

        //remove us when we've visited our children
        context.Lineage.RemoveLast();
    }
}

现在,您应该能够通过层次结构跟踪访问。这要归功于递归调用,这些调用将在遍历层次结构时继续构建面包屑。

答案 1 :(得分:0)

感谢jgauffin的提示! 但是我按照我想要的方式做了一些小事。

开始使用泛型。

public abstract class Node
{
    public abstract T Accept<T>(IVisitor<T> visitor);
}

public interface IVisitor<T>
{
    T Visit(And element);
    T Visit(Or element);
    T Visit(Equals element);
}

所以我的访问者可以实现它而不用担心这样的对象:

public string Visit(Or element)
{
   return "Or(" + element.Left.Accept(this) + "," + element.Right.Accept(this) + ")";
}

所以我的程序只接受根节点并打印出我之前提到的字符串。