Visitor Design Pattern

reference by Visitor design pattern

Intent

意图

  • Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.
  • 表示要在对象结构的元素上执行的操作。Visitor允许您定义一个新的操作,而不需要更改它操作的元素的类。
  • The classic technique for recovering lost type information.
  • 用于恢复丢失类型信息的经典技术。
  • Do the right thing based on the type of two objects.
  • 根据两个对象的类型做正确的事情。
  • Double dispatch
  • 双重分发

Problem

问题

Many distinct and unrelated operations need to be performed on node objects in a heterogeneous aggregate structure. You want to avoid “polluting” the node classes with these operations. And, you don’t want to have to query the type of each node and cast the pointer to the correct type before performing the desired operation.

在异构聚合结构中,需要在节点对象上执行许多不同的和不相关的操作。您希望避免使用这些操作“污染” node 类。而且,您不需要查询每个 node 的类型,并在执行所需的操作之前将 pointer 转换为正确的类型。

Discussion

讨论

Visitor’s primary purpose is to abstract functionality that can be applied to an aggregate hierarchy of “element” objects. The approach encourages designing lightweight Element classes - because processing functionality is removed from their list of responsibilities. New functionality can easily be added to the original inheritance hierarchy by creating a new Visitor subclass.

访问者的主要目的是抽象可以应用于“元素”对象的聚合层次结构的功能。这种方法鼓励设计轻量级元素类——因为处理功能从它们的职责列表中删除。通过创建新的访问者子类,可以很容易地将新功能添加到原始的继承层次结构中。

Visitor implements “double dispatch”. OO messages routinely manifest “single dispatch” - the operation that is executed depends on: the name of the request, and the type of the receiver. In “double dispatch”, the operation executed depends on: the name of the request, and the type of TWO receivers (the type of the Visitor and the type of the element it visits).

访问者实现“双重分发”。OO messages 通常表现为“单分派”——执行的操作依赖于: request 的 name 和 receiver 的 type。在“双重分发”中,执行的操作依赖于: request 的name,以及两个 receiver 的 type(Visitor 的 type 和它访问的 element 的 type)

The implementation proceeds as follows. Create a Visitor class hierarchy that defines a pure virtual visit() method in the abstract base class for each concrete derived class in the aggregate node hierarchy. Each visit() method accepts a single argument - a pointer or reference to an original Element derived class.

执行情况如下。创建一个 Visitor 类层次结构,在聚合节点层次结构中的每个具体派生类的抽象基类中定义纯虚拟 visit() 方法。每个 visit() 方法接受一个参数—一个指向原始元素派生类的指针 pointer引用 reference

Each operation to be supported is modelled with a concrete derived class of the Visitor hierarchy. The visit() methods declared in the Visitor base class are now defined in each derived subclass by allocating the “type query and cast” code in the original implementation to the appropriate overloaded visit() method.

所支持的每个操作都是用 Visitor 层次结构的一个具体派生类来建模的。通过将“ type 查询和 cast ”代码分配到适当的重载 visit() 方法中,在每个派生子类中定义了访问者基类中声明的 visit() 方法。

Add a single pure virtual accept() method to the base class of the Element hierarchy. accept() is defined to receive a single argument - a pointer or reference to the abstract base class of the Visitor hierarchy.

向 Element 层次结构的基类添加一个纯虚拟 accept() 方法。accept() 被定义为接收单个参数——指向 Visitor 层次结构抽象基类的 指针 pointer引用reference

Each concrete derived class of the Element hierarchy implements the accept() method by simply calling the visit() method on the concrete derived instance of the Visitor hierarchy that it was passed, passing its “this” pointer as the sole argument.

Element 层次结构的每一个具体的派生类都实现了 accept() 方法,简单地将 visit() 方法称为访问层次结构的具体派生实例,通过它传递的“this”指针作为唯一的参数。

Everything for “elements” and “visitors” is now set-up. When the client needs an operation to be performed, (s)he creates an instance of the Visitor object, calls the accept() method on each Element object, and passes the Visitor object.

“元素”和“访客”的一切都是建立起来的。当 client 需要执行一个操作时,他会创建一个访问者对象的实例,在每个元素对象上调用accept() 方法,并传递 Visitor 对象。

The accept() method causes flow of control to find the correct Element subclass. Then when the visit() method is invoked, flow of control is vectored to the correct Visitor subclass. accept() dispatch plus visit() dispatch equals double dispatch.

accept() 方法导致控制流找到正确的 Element 子类。然后当调用 visit() 方法时,控件的流将向正确的 Visitor subclass.accept() 分派加 visit() 分派等于双分派。

The Visitor pattern makes adding new operations (or utilities) easy - simply add a new Visitor derived class. But, if the subclasses in the aggregate node hierarchy are not stable, keeping the Visitor subclasses in sync requires a prohibitive amount of effort.

访问者模式使得添加新的操作(或实用程序)变得简单——只需添加一个新的 Visitor 派生类。但是,如果聚合节点层次结构中的 subclasses 不稳定,那么保持访问者子类的同步需要花费大量的精力。

An acknowledged objection to the Visitor pattern is that is represents a regression to functional decomposition - separate the algorithms from the data structures. While this is a legitimate interpretation, perhaps a better perspective/rationale is the goal of promoting non-traditional behavior to full object status.

对于访问者模式的一个公认的反对意见是,这代表了对功能分解的回归——将算法从数据结构中分离出来。虽然这是一个合理的解释,但也许一个更好的视角/理由是将非传统行为推广到完全对象状态的目标。

Structure

数据结构

The Element hierarchy is instrumented with a “universal method adapter”. The implementation of accept() in each Element derived class is always the same. But – it cannot be moved to the Element base class and inherited by all derived classes because a reference to this in the Element class always maps to the base type Element.

Element层次结构用“通用方法适配器”来检测。每个元素派生类中的accept()的实现总是相同的。但是,它不能被移动到 Element 基类,并且继承到所有派生类,因为 Element 类中的这个引用总是映射到基本类型元素。

Visitor Structure

When the polymorphic firstDispatch() method is called on an abstract First object, the concrete type of that object is “recovered”. When the polymorphic secondDispatch() method is called on an abstract Second object, its concrete type is “recovered”. The application functionality appropriate for this pair of types can now be exercised.

visitor structure2

Example

The Visitor pattern represents an operation to be performed on the elements of an object structure without changing the classes on which it operates. This pattern can be observed in the operation of a taxi company. When a person calls a taxi company (accepting a visitor), the company dispatches a cab to the customer. Upon entering the taxi the customer, or Visitor, is no longer in control of his or her own transportation, the taxi (driver) is.

examle

Check list

  1. Confirm that the current hierarchy (known as the Element hierarchy) will be fairly stable and that the public interface of these classes is sufficient for the access the Visitor classes will require. If these conditions are not met, then the Visitor pattern is not a good match.
  2. Create a Visitor base class with a visit(ElementXxx) method for each Element derived type.
  3. Add an accept(Visitor) method to the Element hierarchy. The implementation in each Element derived class is always the same – accept( Visitor v ) { v.visit( this ); }. Because of cyclic dependencies, the declaration of the Element and Visitor classes will need to be interleaved.
  4. The Element hierarchy is coupled only to the Visitor base class, but the Visitor hierarchy is coupled to each Element derived class. If the stability of the Element hierarchy is low, and the stability of the Visitor hierarchy is high; consider swapping the ‘roles’ of the two hierarchies.
  5. Create a Visitor derived class for each “operation” to be performed on Element objects. visit() implementations will rely on the Element’s public interface.
  6. The client creates Visitor objects and passes each to Element objects by calling accept().

Rules of thumb

  1. The abstract syntax tree of Interpreter is a Composite (therefore Iterator and Visitor are also applicable).
  2. Iterator can traverse a Composite. Visitor can apply an operation over a Composite.
  3. The Visitor pattern is like a more powerful Command pattern because the visitor may initiate whatever is appropriate for the kind of object it encounters.
  4. The Visitor pattern is the classic technique for recovering lost type information without resorting to dynamic casts.

Notes

The November 2000 issue of JavaPro has an article by James Cooper (author of a Java companion to the GoF) on the Visitor design pattern. He suggests it “turns the tables on our object-oriented model and creates an external class to act on data in other classes … while this may seem unclean … there are good reasons for doing it.”

His primary example. Suppose you have a hierarchy of Employee-Engineer-Boss. They all enjoy a normal vacation day accrual policy, but, Bosses also participate in a “bonus” vacation day program. As a result, the interface of class Boss is different than that of class Engineer. We cannot polymorphically traverse a Composite-like organization and compute a total of the organization’s remaining vacation days. “The Visitor becomes more useful when there are several classes with different interfaces and we want to encapsulate how we get data from these classes.”

His benefits for Visitor include:

  1. Add functions to class libraries for which you either do not have the source or cannot change the source
  2. Obtain data from a disparate collection of unrelated classes and use it to present the results of a global calculation to the user program
  3. Gather related operations into a single class rather than force you to change or derive classes to add these operations
  4. Collaborate with the Composite pattern

Visitor is not good for the situation where “visited” classes are not stable. Every time a new Composite hierarchy derived class is added, every Visitor derived class must be amended.

猜你喜欢

转载自blog.csdn.net/fudaxing/article/details/78260259