1. 程式人生 > >Visitor Design Pattern

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.