访客设计模式和深度优先搜索之间的区别?
深度优先搜索似乎能够执行与访问者设计模式类似的功能。访问者允许您定义一些数据结构,并根据需要在这些结构上添加操作(以多个访问者的形式),而无需修改结构本身。 wikipedia 上提供了访问者模式的描述。如果我们对数据结构进行深度优先搜索(或任何其他图搜索算法,如广度优先搜索),并且每次找到结构的元素时,我们都会运行所需的操作,那么这似乎执行与访客。例如,考虑一棵树。即使树的某些节点具有不同的类型,我们仍然可以在进行DFS时检查节点类型,然后根据节点类型执行不同的操作。
A depth first search seem able to perform similar functions as the visitor design pattern. A visitor allows you to define some data structures and add operations on those structures (in the form of multiple visitors) as needed without modifying the structures themselves. A description of the visitor pattern is provided on wikipedia. If we do a depth first search (or any other graph search algorithm like breadth first search) on the data structure, and every time an element of the structure is found, we run our desired operation, then this seems to perform the same function as the visitor. For example, consider a tree. Even if some nodes of the tree have different type, we can still check for the node types when doing DFS and then perform different operations based on the node type.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
深度优先搜索就是搜索。访问者模式与深度优先搜索正交,从某种意义上说,访问者不一定关心如何遍历树;它只知道需要在每个节点上/对每个节点做什么。
A depth-first search is just that--a search. A Visitor pattern is orthogonal to a depth-first search, in the sense that a Visitor doesn't necessarily care how the tree is traversed; it just knows what it needs to do on/to/with each node.
您可以在没有 DFS 的情况下实现访客。同样,您可以在不使用访问者模式的情况下执行 DFS。它们是完全分开的。
我碰巧同意它们可以以一种优雅的方式一起使用的含义。
请注意,对于规范的访问者模式,被访问的对象需要实现某种
AcceptVisitor
接口 - 您问题中的“不修改结构本身”子句让我怀疑您是否在做那。You can have Visitor implementation without having DFS. Similarly, you can do a DFS without use of the Visitor pattern. They are completely separate.
I happen to agree with the implication that they could be used together in an elegant way.
As a note, for the canonical Visitor pattern the objects being visited need to implement some sort of
AcceptVisitor
interface -- the clause "without modifying the structures themselves" in your question leads me to question whether you are doing that.让我用代码来回答这个问题:
在本例中,visitNodes() 方法实现了深度优先搜索,但它不是必须的。它可以实现任何命中所有节点的搜索。它是 VisitNodes() 方法和 NodeVisitor 接口的组合,构成了“访问者模式”(或其一种特定表现形式)。
设计模式和搜索算法之间没有“权衡”。设计模式只是让算法更容易使用。
Let me answer the question in code:
In this case the visitNodes() method implements a depth-first search, but it doesn't have to. It could implement any search that hits all nodes. It's the combination of the visitNodes() method and the NodeVisitor interface that comprise the "visitor pattern" (or one particular manifestation of it.)
There's no "tradeoff" between the design pattern and the search algorithm. The design pattern just makes the algorithm easier to use.
在图论中,您可以构造一个图,使得最小生成树不是来自深度优先搜索的路径,更确切地说,它是一个非路径:https://cs.stackexchange.com/questions/6749/深度-首先搜索找到最小生成树。我认为你不能将其应用于设计模式。
In graph theory you can construct a graph such that the minimum spanning tree is not a path from a depth-first search, precisley it's a non-path:https://cs.stackexchange.com/questions/6749/depth-first-search-to-find-minimum-spanning-tree. I think you cannot apply this to a design pattern.
深度优先搜索是一种“算法”,而访问者模式是一种忘记算法问题并专注于要执行的操作的方法。
实际上,访问者模式可能是索引内容的好方法,因为它提供了“结构不可知”的行为(您可以更改结构而无需重写访问者)
但是如果您想要执行搜索,我不建议您使用它。任何搜索算法都与特殊类型的结构(树、有向图、流程图等)相关。
在某些情况下,您可以使用访问者模式实现深度优先搜索 >,但这不是这个模式的目的。
访问者模式的使用并不取决于您所使用的解析类型,而是取决于必须执行解析的目的。
Depth first seach is an "algorithm" and the visitor pattern is a way to forget algorithm concerns and focus on actions to perform.
Actually, the visitor Pattern may be a good way to index content, as it provides a "structure-agnostic" behaviour (you can change structure without rewriting visitors)
But if you want to perform a search, I would not advise you to use it. Any search algorithm is related to a special type of structure (tree, digraph, flow graphs, etc)
In some cases, you can achieve a depth-first search with a visitor pattern, but this is not the purpose of this pattern.
The use of visitor pattern doesn't depend of what kind of parsing you are using, but what the parsing must be performed for.
visitor
实例可以选择按照自己的意愿访问其子节点,并且不受深度优先搜索的遍历顺序的约束。 (例如,它可以使用呼吸优先搜索)另一件事是要遍历的结构不需要是显式树。我已经实现了访问者,它迭代根本不像树的结构化数据。在这种情况下,我使用了访问者,因为我可以隐藏正在解析的文件的复杂二进制格式,并允许客户端控制他们想要解析的结构的哪些部分,而不需要他们知道文件格式规范。
The
visitor
instance may choose to visit its child nodes however it wishes and is not bound by the traversal order of Depth First Search. (For example it could use Breath First Search)Another thing to mention is the structure getting traversed does not need to be an explicit tree. I've implemented Visitors that iterate over structured data that wasn't tree like at all. I used a visitor in that instance because I could hide the complicated binary format of the file I was parsing and allow clients to control which parts of the structure they wanted to parse without needing them to know the file format specification.
我也同意 NRITH 的答案,因为访问者模式知道“只对节点做什么” 在该访问者之上,让您“定义一个新操作,而无需更改其上元素的类”它运行',DFS 讨论如何执行节点搜索。但是为了执行深度遍历和短路分支遍历,我们使用分层访问者模式( http://c2.com/cgi/wiki?HierarchicalVisitorPattern)。
您还可以查看我什么时候应该使用访客设计Pattern?讲的是访问者模式/DFS/分层访问者模式之间的关系。
I too go with NRITH's answer as visitor pattern know 'only what to do with the node' on top of that Visitor lets you 'define a new operation without changing the classes of the elements on which it operates' and the DFS talks about how the node search is performed. But for performing depth traversal and short-circuit branch traversal we use Hierarchical Visitor pattern(http://c2.com/cgi/wiki?HierarchicalVisitorPattern).
You can also look into When should I use the Visitor Design Pattern? which talks about the relation between visitor pattern/DFS/Hierarchical visitor pattern.
访问者模式(第一个?)在 Erich Gamma 等人的“设计模式”中描述。等人。在accept方法中并不一定包括对数据结构的遍历。虽然这是一个非常方便的组合,但在本书的示例代码章节末尾有一个明确的外部迭代示例。
因此,正如其他人已经说过的,在接受方法之外实现的深度优先遍历仍然可以实现访问者模式。那么问题是,调用 element.accept(visitor) 进而直接调用访问者.visitElement(me) 与直接调用访问者.visitElement(element) 之间有什么区别?
我只能看到人们想要这样做的两个原因:
您不能或不想找出元素的具体类,并且通过愚蠢地调用 element.accept(visitor),元素本身必须决定,无论是visitor.visitElement还是例如visitor.visitAnotherElement都是要调用的正确操作。
某些元素是复合元素,无需外部访问所包含的内部元素,并且访问者操作是为内部元素定义的。因此,accept 方法将循环遍历内部元素并调用 Visitor.visitInnerElement(innerElement)。由于您无法从外部获取内部元素,因此您也无法从外部调用访问者.visitInnerElememt(innerElement)。
总结:如果你有一个封装得很好的遍历算法,你可以传入一个类似“Visitor”的类,并且能够根据遍历过程中遇到的对象类型分派到匹配的访问方法,那么你不需要担心接受方法。您仍然可以通过添加新的访问者实现来添加新的操作,而无需触及您的数据结构或遍历算法。这是否仍应称为访问者模式是一个相当学术性的讨论。
我认为在大多数情况下,只有当接受方法的实现也包括遍历时,使用接受方法才有意义。
The visitor pattern as (first?) described in "Design Patterns" by Erich Gamma et. al. does not necessarily include the traversal through the data structure in the accept method. Although this is a very convenient combination, there is an explicit example for external iteration at the end of the Sample Code chapter in the book.
So as others already said, a depth first traversal implemented outside of the accept method could still implement the Visitor Pattern. The question is then, what's the difference between calling element.accept(visitor) which in turn directly calls visitor.visitElement(me) compared to directly calling visitor.visitElement(element)?
I can see only two reasons one may want to do that:
You can not or do not want to find out the concrete class of element, and by just stupidly calling element.accept(visitor), the element itself has to decide, whether visitor.visitElement or e.g. visitor.visitAnotherElement is the right operation to be called.
Some of the elements are composites without external access to the contained inner elements, and the visitor operations are defined for the inner elements. So the accept method would loop over the inner elements and would call visitor.visitInnerElement(innerElement). And as you do not get hold of the inner elements from outside, you also can not call visitor.visitInnerElememt(innerElement) from outside.
In summary: If you have a nicely encapsulated traversal algorithm, where you can pass in a "Visitor"-like class, and which is able to dispatch to the matching visit methods depending on the object type encountered during traversal, you do not need to bother about accept-methods. You will still be able to add new operations by just adding new Visitor implementations, and without touching your data structure nor your traversal algorithm. Whether this should still be called Visitor Pattern is a pretty academic discussion.
I think in most cases bothering with an accept method makes only sense if implementation of the accept method also includes the traversal.