410 likes | 444 Views
Learn about the Visitor Design Pattern by John Vlissides that allows adding new operations to object structures without changing their classes. Explore motivations, implementation details, participants, and consequences of using the pattern.
E N D
The Visitor Pattern EXISTS! • And its INTENT is to represent an operation to be performed on the elements of an object structure.
How? • By letting the user define a new operation without changing the classes of the elements on which it operates!
Motivation • Consider a compiler with abstract syntax trees...
Abstract Syntax Trees • Compiler may need to define operations for: Type-Checking Code Optimization Flow Analysis Variable Checking ...and more!
Meaning.... • There will be one class for every single thing that needs to be performed... • Distributing operations amongst various node classes is hard to understand, maintain and change! • Adding a new operation would be a pain in the butt
What we want... • Each new operation should be added separately • All of the node classes to be independent of their operations
The Solution • Package related operations from each class in a separate object, and pass it to elements of the abstract syntax tree as it is traversed. We call this object...
On a serious note • When an element accepts a visitor, it sends a request to it that encodes the element’s class. • It also includes the element as an argument. • The visitor is left to execute the operation for that element, which used to be in the class of the element.
This is important too • To make visitors work for more than just type-checking, we need an abstract parent NodeVisitor classfor all visitors of an abstract syntax tree.
NodeVisitor • NodeVisitor must declare an operation for each node class.
Visitor – Up Close and Personal • There are two class hierarchies with this pattern (page 332 – 333): Node - for elements being operated on NodeVisitor - for the visitors that define operations on the elements
Visitor, continued • A new operation is created by adding a new subclass in the visitors class hierarchy. • New functionality can simply be added by defining new NodeVisitor subclasses.
You know you wanna use this when... • An object structure contains many classes of objects with differing interfaces, and you want to perform operations on these objects that depend on their concrete classes. • Many distinct and unrelated operations need to be performed on objects in an object structure and you don’t want to “pollute” their classes with these operations. • The classes defining the object structure rarely change, but you often want to define new operations over the structure.
Participants • Visitor – declares a visit operation for each class of ConcreteElement in the object structure • ConcreteVisitor (PriceVisitor) – implements each operation declared by Visitor • Element (Trash) – defines an Accept operation that takes a visitor as an argument
More Participants! • ConcreteElement (Aluminum, Paper, Glass) – implements an Accept operation that takes a visitor as an argument • ObjectStructure (next to Trash) - can enumerate its elements - may provide a high-level interface to allow the visitor to visit its elements - may be a composite or a collection like a set or list.
Collaborations • A client that uses Visitor must create a ConcreteVisitor object and then traverse the object structure, visiting each element with the visitor (page 335) • When an element is visited, it calls the visitor operation that corresponds to its class. The element supplies itself as an argument to this operation to let the visitor access its state, if necessary.
Consequences 1. Visitor makes adding new operations easy! 2. A Visitor gathers related operations and separates unrelated ones, simplifying classes defining elements and algorithms defined by visitors. 3. Adding new ConcreteElement classes is hard. 4. Visitor does not have access restrictions like an iterator does. It can visit objects that don’t have a common parent class, and you can add any type to a visitor interface. 5. Visitors can accumulate state as they visit each element in the object structure. 6. The Visitor pattern often forces you to provide public operations that access an element’s internal state, which may compromise encapsulation.
The Implementation Is on page 337.
Technical Details • Double Dispatch – Visitor uses this to let us add operations to classes without changing them. Not all programming languages support it directly (like C++). • It means that operations get executed depending on the kind of request and types of two receivers, NOT one.
This is Crucial! • Because of double dispatch, an executed operation depends on the type of Visitor and the Element it visits. • Binding operations can be done at run-time with “Accept” in lieu of statically with the element interface.
And who is responsible ...for object structure traversal? After all, we know that a visitor must visit each element of the object structure.
Hmmm...... • Responsibility can fall on: • 1) the structure, • 2) the visitor, or • 3) a separate iterator • Most common is to use the structure itself, but an iterator is used just as effectively. • The visitor is used least often to do it, because traversal code often gets duplicated.
Usage • Visitor is not used often, at least according to www.dofactory.com and www.nice.sourceforge.net/visitor.html • The Second site considers it totally useless. • But don’t let that discourage you, because it’s a cool pattern.
And lastly... • Visitor can be used to apply an operation over an object structure defined by the composite pattern. • Visitor can also do the interpretation for the Interpreter pattern.
Visiting Rights • So far we’ve used Composite and Proxyfor supporting symbolic links. • See Figure 2.3, page 29 • Composite contributed Node, File and Directory classes • Proxy contributed the Link class.
Alexandrian Density • The Node class is the Component in the COMPOSITE pattern and the Subject in the PROXY pattern. • Christopher Alexander (above) called this “density”. • So he got the term named after him. • Lucky guy.
Our File System • The Node Interface is up for discussion • We want a minimal set of operations that lets clients build open-ended functionality. • “Performing surgery” on the interface would be a hassle every time we wanted to add something new to it.
Is there an alternative? • What if we take functionality out of the node classes and put it into a client? • We need Downcasts to do this, making the client complicated.
But Downcasts are yucky! Dynamic_cast<File*>(node)
Visitor...TO THE RESCUE! • Our file system problem is similar to the abstract syntax tree example • By using the accept method like we did before, we let the visitor pattern do the work for us. • Bottom line: No type tests or downcasting
More on this • Once we establish visiting rights by adding accept(Visitor &) to Node classes, those classes NEVER need to be modified again. • Vlissides uses function overloading, but encoding the type of Node in the visit operation’s name works just as well (page 35-36)
Is the Visitor right for you? • The Visitor pattern isn’t for everyone. Consult your textbook to find out if it’s the pattern that you’re looking for.
Things to Ask Yourself • Is the class hierarchy I’m visiting stable? Or will it be changing like crazy? “Adding a new kind of Node may force us to change all the classes in the Visitor hierarchy just to add a corresponding visit operation.” • Will the circular dependency between Visitor and Node class hierarchies bother you? A change to either base class interface is likely to prompt a recompile of both hierarchies.
Kevlin Henney: The guy who was referenced on Page 37 • “C++ Overloading does not require having to overload all versions of visit or that you abandon overloading the visit member.”
Valuable Insight • The “using” declaration allows us to inject names from a base class into a class for overloading. • This maintains overloading regularity. • And it’s noninvasive because users are not forced to remember names or conventions from the visit function.
Up Next: Security • 1) Protecting the system from inadvertent and malicious corruption • 2) Maintaining system integrity in the face of hardware and software errors.
Any Questions? Manne or Nick would love to answer them.
Credits The “Gang”: Erich Gamma Richard Helm Ralph Johnson John Vlissides Good TA’s: Chris Swope Yuling Liang Patterns Mentioned: Visitor Composite Proxy Interpreter Inspiration Provided By: The Nick Yianilos Design Pattern Experience Thanks For Watching: CSE 432 Good Teacher: Chris Gill References: Christopher Alexander Kevlin Henney