访问者模式和逻辑操作
Visitor pattern and logical operations
好吧,我一直在努力解决我遇到的这个问题。
我创建了一个包含逻辑节点的树结构,例如和,或,等于,之间。
我不希望这些节点对访问者有一个接受方法,因为会有更多的访问者有不同的实现。
例如,我正在访问 and。它首先访问自己,所以我知道我正在处理一个和,然后它访问左右节点。可以是前面提到的任何节点。
问题是,我需要一种方法来检查我是否已完成对子节点的访问。
例如我想要这个输出
"And(Equals()/Or(Equals()/Between())"
但是因为我无法知道孩子们什么时候被拜访过所以会是这样的。
"And()Equals()/Or()Equals()/Between()
关于如何克服这个问题有什么建议吗?
因此您需要跟踪访问节点时您在树中的位置。为此,您需要引入某种上下文。
像这样:
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);
}
现在你只需要遍历它。关键是要有一个受保护的重载,告诉 children 它们是哪种类型。请注意递归调用。
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);
}
}
现在你知道你在树上什么时候左右走,但你仍然不知道你在树上的什么位置。为此,让我们引入一个 lineage/breadcrumbs 来跟踪我们当前的路径。
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();
}
}
现在您应该能够通过层次结构跟踪访问。这要归功于递归调用,递归调用将在遍历层次结构时继续构建面包屑。
感谢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) + ")";
}
所以我的程序只接受根节点并将打印出我之前提到的字符串。
好吧,我一直在努力解决我遇到的这个问题。
我创建了一个包含逻辑节点的树结构,例如和,或,等于,之间。 我不希望这些节点对访问者有一个接受方法,因为会有更多的访问者有不同的实现。
例如,我正在访问 and。它首先访问自己,所以我知道我正在处理一个和,然后它访问左右节点。可以是前面提到的任何节点。
问题是,我需要一种方法来检查我是否已完成对子节点的访问。 例如我想要这个输出
"And(Equals()/Or(Equals()/Between())"
但是因为我无法知道孩子们什么时候被拜访过所以会是这样的。
"And()Equals()/Or()Equals()/Between()
关于如何克服这个问题有什么建议吗?
因此您需要跟踪访问节点时您在树中的位置。为此,您需要引入某种上下文。
像这样:
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);
}
现在你只需要遍历它。关键是要有一个受保护的重载,告诉 children 它们是哪种类型。请注意递归调用。
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);
}
}
现在你知道你在树上什么时候左右走,但你仍然不知道你在树上的什么位置。为此,让我们引入一个 lineage/breadcrumbs 来跟踪我们当前的路径。
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();
}
}
现在您应该能够通过层次结构跟踪访问。这要归功于递归调用,递归调用将在遍历层次结构时继续构建面包屑。
感谢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) + ")";
}
所以我的程序只接受根节点并将打印出我之前提到的字符串。