在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
ProblemMany 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. DiscussionVisitor’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). The implementation proceeds as follows. Create a Visitor class hierarchy that defines a pure virtual Each operation to be supported is modelled with a concrete derived class of the Visitor hierarchy. The Add a single pure virtual Each concrete derived class of the Element hierarchy implements the 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 Vistor object, calls the The 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. 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. StructureThe Element hierarchy is instrumented with a “universal method adapter”. The implementation of When the polymorphic ExampleThe 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. Check list
Rules of thumb
NotesThe 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:
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. Vistor in DelphiThis session consists of the development of a small application to read and pretty-print XML and CSV files. Along the way, we explain and demonstrate the use of the following patterns: State, Interpreter, Visitor, Strategy, Command, Memento, and Facade. 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. What the Visitor pattern does is move the operations on the tree (or other object structure) from the nodes of the tree to another class. This class needs enough information in the interface of each node to perform the operation, so sometimes this can mean more public properties and methods than you might like. Visitors have been described in Delphi before so we won’t dwell overly on them. Essentially, what you need to do is declare a Visitor class, which declares a Visit operation for each node type in the object structure. Here’s the base visitor class for the XML interpreter: 1: TXmlInterpreterVisitor = class(TObject)2: private3: protected4: procedure Visit(Exp : TXmlStartTag); overload; virtual;5: procedure Visit(Exp : TXmlEndTag); overload; virtual;6: procedure Visit(Exp : TXmlNode); overload; virtual;7: procedure Visit(Exp : TXmlTagList); overload; virtual;8: procedure Visit(Exp : TXmlProlog); overload; virtual;9: procedure Visit(Exp : TXmlDoc); overload; virtual;10: public Visit methods are virtual so that visitor descendants can choose which methods to implement - it may not be necessary to implement them all. I’ve used function overloading as the signature is of necessity different for each, but this is not essential, and you could use more explicit names e.g. In the base expression class we need to define an abstract
The only difference is that we call the Once the visitor code is in place in the syntax tree code, adding new visitors does not require any more changes to that code, only the declaration of new visitor classes. A concrete visitor class is defined in XmlInterpreterVisitors.pas, showing how to implement a pretty printer. The parser is quite capable of taking a very badly formatted XML file and creating the syntax tree. We will regenerate the document all nicely laid out, like the example XML we saw earlier. The class definition is: 1: TXmlPrettyPrinter = class(TXmlInterpreterVisitor)2: private3: FList : TStringList;4: FIndent : Integer;5:6: function GetText : string;7: protected8: procedure AddString(AStr : string);9: public As you can see, we don’t need to implement a visitor for every expression class, as not all of them require anything to be printed. In fact, it’s only those that have terminal sub-expressions that will get anything printed. That is, the start and end tags, the data in a node, and the prolog. We’ll keep track of the indent at each point, and on each new line we will prepend the correct number of spaces. I’ve decided to collect the newly formatted text in a TStringList as it will keep track of new lines for me. The Some of the
On finding a start tag, we add the tag to the list, then increment the indent. On an end tag we do the reverse, decrementing first to bring it back into line with the start tag. We also add a blank line after the tag. As it happens, this will only be the case for tags surrounding XML collections, as the place I cheated is on individual data nodes. I arranged the Adding a new operation on the syntax tree is easy, we just add a new visitor (we could reimplement the search and replace this way, for instance). Now related operations are all in one class, and unrelated ones would be in different classes, rather than the Interpreter classes containing many unrelated operations in each class. Visitor is a really nice pattern, and quite often useful. It is similar to Iterator in that it is used to traverse object structures, but Visitor also works when there is no common parent for the structure items. However, it does have some disadvantages. We mentioned breaking encapsulation earlier, but it can also make life difficult if you often need to add new elements to your structure. For instance, if we added new grammar rules, then we would need to add new methods to the base visitor, and check every concrete visitor to see if it also needed to reflect the changes. So we can now read both XML and CSV files. It’s time to start feeding them documents. |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论