访问者设计模式和深度优先搜索之间的区别?

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 方法的实现也包括遍历时才有意义。

【讨论】:

以上是关于访问者设计模式和深度优先搜索之间的区别?的主要内容,如果未能解决你的问题,请参考以下文章

深度优先搜索法和广度优先搜索法

深度优先搜索

你知道吗?广度优先与深度优先只有这一个区别!

你知道吗?广度优先与深度优先只有这一个区别!

图的优先遍历:广度优先搜索和深度优先搜索

深度优先遍历与广度优先遍历的区别