访问者设计模式和深度优先搜索之间的区别?
Posted
技术标签:
【中文标题】访问者设计模式和深度优先搜索之间的区别?【英文标题】:Difference between visitor design pattern and depth first search? 【发布时间】:2011-09-12 01:11:55 【问题描述】:深度优先搜索似乎能够执行与访问者设计模式类似的功能。访问者允许您根据需要定义一些数据结构并在这些结构上添加操作(以多个访问者的形式),而无需修改结构本身。 wikipedia 提供了访问者模式的描述。如果我们对数据结构进行深度优先搜索(或任何其他图搜索算法,如广度优先搜索),并且每次找到结构的元素时,我们都会运行我们想要的操作,那么这似乎执行相同的功能访客。例如,考虑一棵树。即使树的某些节点类型不同,我们仍然可以在做 DFS 时检查节点类型,然后根据节点类型执行不同的操作。
【问题讨论】:
【参考方案1】:深度优先搜索就是一个搜索。访问者模式与深度优先搜索正交,因为访问者不一定关心如何遍历树;它只知道在每个节点上/对/对每个节点需要做什么。
【讨论】:
【参考方案2】:您可以在没有 DFS 的情况下实现访问者。同样,您可以在不使用访问者模式的情况下进行 DFS。它们是完全分开的。
我碰巧同意它们可以以一种优雅的方式一起使用的暗示。
作为说明,对于规范的访问者模式,被访问的对象需要实现某种AcceptVisitor
接口——您问题中的“不修改结构本身”子句让我质疑您是否正在这样做。
【讨论】:
【参考方案3】:让我用代码回答问题:
/**
* This method makes it easier for a class to process all
* the nodes in an item. The class should implement
* the NodeVisitor interface. This method will cause the
* NodeVisitor.processNode() method to be called once for each
* node in this item.
* @param visitor the class that will visit each node
* @throws PipelineException some recoverable exception
*/
public void visitNodes(NodeVisitor visitor) throws PipelineException
visitNodes(visitor, root);
private void visitNodes(NodeVisitor visitor, Node node) throws PipelineException
visitor.processNode(node);
if (node.hasChildren())
int childCount = node.getChildCount();
NodeList children = node.getChildren();
for (int i = 0; i < childCount; i++)
visitNodes(visitor, children.get(i));
/**
* Classes that implement this interface can be used with
* Item.visitNodes(). This method provides a convenient
* way to iterate over all the nodes in an item.
*/
public interface NodeVisitor
public void processNode(Node node) throws PipelineException;
在这种情况下,visitNodes() 方法实现了深度优先搜索,但它不是必须的。它可以实现任何命中所有节点的搜索。 visitNodes() 方法和 NodeVisitor 接口的组合构成了“访问者模式”(或它的一种特定表现形式)。
设计模式和搜索算法之间没有“权衡”。设计模式只是让算法更易于使用。
【讨论】:
【参考方案4】:在图论中,您可以构造一个图,使得最小生成树不是深度优先搜索的路径,确切地说它是非路径:https://cs.stackexchange.com/questions/6749/depth-first-search-to-find-minimum-spanning-tree。我认为您不能将其应用于设计模式。
【讨论】:
【参考方案5】:深度优先搜索是一种“算法”,访问者模式是一种忘记算法问题并专注于要执行的操作的方法。
实际上,访问者模式可能是索引内容的好方法,因为它提供了一种“结构无关”的行为(您可以在不重写访问者的情况下更改结构)
但如果你想进行搜索,我不建议你使用它。任何搜索算法都与一种特殊类型的结构(树、有向图、流程图等)相关
在某些情况下,您可以使用访问者模式实现深度优先搜索,但这不是此模式的目的。
访问者模式的使用并不取决于您使用的是哪种解析,而是必须针对什么执行解析。
【讨论】:
【参考方案6】:visitor
实例可以根据自己的意愿选择访问其子节点,并且不受深度优先搜索的遍历顺序的约束。 (例如它可以使用呼吸优先搜索)
另外要提到的是,被遍历的结构不需要是一个显式的树。我已经实现了对根本不像树的结构化数据进行迭代的访问者。我在那个例子中使用了一个访问者,因为我可以隐藏我正在解析的文件的复杂二进制格式,并允许客户端控制他们想要解析的结构的哪些部分,而无需他们知道文件格式规范。
【讨论】:
【参考方案7】:我也同意 NRITH 的回答,因为访问者模式知道 '只有如何处理节点' 在访问者之上让您 '定义新操作而不更改类它操作的元素' 和 DFS 讨论如何执行节点搜索。但是为了执行深度遍历和短路分支遍历,我们使用分层访问者模式(http://c2.com/cgi/wiki?HierarchicalVisitorPattern)。
您也可以查看When should I use the Visitor Design Pattern?,它讨论了访问者模式/DFS/分层访问者模式之间的关系。
【讨论】:
【参考方案8】:访问者模式(首先?)由 Erich Gamma 等人在“设计模式”中描述。人。不一定包括在accept方法中遍历数据结构。虽然这是一个非常方便的组合,但在本书示例代码一章的末尾有一个明确的外部迭代示例。
正如其他人已经说过的,在接受方法之外实现的深度优先遍历仍然可以实现访问者模式。那么问题来了,调用 element.accept(visitor) 并直接调用 visitor.visitElement(me) 与直接调用 visitor.visitElement(element) 有什么区别? 我只能看到人们想要这样做的两个原因:
您不能或不想找出元素的具体类,并且只需愚蠢地调用 element.accept(visitor),元素本身就必须决定是visitor.visitElement 还是例如visitor.visitAnotherElement 是要调用的正确操作。
一些元素是组合的,没有外部访问所包含的内部元素,并且访问者操作是为内部元素定义的。所以accept方法会遍历内部元素并调用visitor.visitInnerElement(innerElement)。而且由于您没有从外部获取内部元素,因此您也无法从外部调用 visitor.visitInnerElememt(innerElement)。
总结:如果你有一个很好封装的遍历算法,你可以传入一个类似“Visitor”的类,并且它能够根据遍历过程中遇到的对象类型分派给匹配的访问方法,你可以不需要担心接受方法。您仍然可以通过添加新的访问者实现来添加新操作,而无需触及您的数据结构或遍历算法。这是否仍应称为访问者模式是一个相当学术的讨论。 我认为在大多数情况下,只有在 accept 方法的实现也包括遍历时才有意义。
【讨论】:
以上是关于访问者设计模式和深度优先搜索之间的区别?的主要内容,如果未能解决你的问题,请参考以下文章