Вы находитесь на странице: 1из 15

Create account

Log in

Article Talk

Read Edit View history

Search

Visitor pattern
From Wikipedia, the free encyclopedia Main page Contents Featured content Current events Random article Donate to Wikipedia Interaction Help About Wikipedia Community portal Recent changes Contact page Tools Print/export Languages

In object-oriented programming and software engineering, the visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures. It is one way to follow the open/closed principle. In essence, the visitor allows one to add new virtual functions to a family of classes without modifying the classes themselves; instead, one creates a visitor class that implements all of the appropriate specializations of the virtual function. The visitor takes the instance reference as input, and implements the goal through double dispatch.
Contents [hide] 1 Motivation 2 Details 3 Java example 3.1 Diagram 3.2 Sources
Visitor in UML

open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

etina Deutsch Espaol Franais Galego Italiano Polski Portugus Svenska Ting Vit Edit links

3.2 Sources 3.3 Output 4 Lisp Example 4.1 Sources 4.2 Output 4.3 Notes 5 Scala Example 5.1 Sources 5.2 Output 5.3 Notes 6 Output 7 State 8 Related design patterns 9 See also 10 References 11 External links
Visitor in LePUS3 (legend )

Motivation

[edit]

Consider the design of a 2D CAD system. At its core there are several types to represent basic geometric shapes like circles, lines and arcs. The entities are ordered into layers, and at the top of the type hierarchy is the drawing, which is simply a list of layers, plus some additional properties. A fundamental operation on this type hierarchy is saving the drawing to the system's native file format. At first glance it may seem acceptable to add local save methods to all types in the hierarchy. But then we also want to be able to save drawings to other file formats, and adding more and more methods for saving into lots of different file formats soon clutters the relatively pure geometric data structure we started out with. A naive way to solve this would be to maintain separate functions for each file format. Such a save function would take a drawing as input, traverse it and encode into that specific file format. But if you do this for several different formats, you soon begin to see lots of duplication between the functions, e.g. lots of type-of if statements and traversal loops. Another problem with this approach is how easy it is to miss a certain shape in some saver. Instead, you could apply the Visitor pattern. The Visitor pattern encodes a logical operation on the whole hierarchy into a single class containing one method per type. In our CAD example, each save function would be implemented as a separate Visitor subclass. This would remove all duplication of type checks and traversal steps. It would also make the compiler

open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

complain if a shape is omitted. Another motivation is to reuse iteration code. For example iterating over a directory structure could be implemented with a visitor pattern. This would allow you to create file searches, file backups, directory removal, etc. by implementing a visitor for each function while reusing the iteration code.

Details

[edit]

The visitor pattern requires a programming language that supports single dispatch and method overloading. Under these conditions, consider two objects, each of some class type; one is called the "element", and the other is called the "visitor". An element has an accept() method that can take the visitor as an argument. The accept() method calls a visit() method of the visitor; the element passes itself as an argument to the visit() method. Thus: When the accept() method is called in the program, its implementation is chosen based on both: The dynamic type of the element. The static type of the visitor. When the associated visit() method is called, its implementation is chosen based on both: The dynamic type of the visitor. The static type of the element as known from within the implementation of the accept() method, which is the same as the dynamic type of the element. (As a bonus, if the visitor can't handle an argument of the given element's type, then the compiler will catch the error.) Consequently, the implementation of the visit() method is chosen based on both: The dynamic type of the element. The dynamic type of the visitor. This effectively implements double dispatch; indeed, because the Lisp language's object system supports multiple dispatch (not just single dispatch), implementing the visitor pattern in Lisp is trivial. In this way, a single algorithm can be written for traversing a graph of elements, and many different kinds of operations can be performed during that traversal by supplying different kinds of visitors to interact with the elements based on the dynamic types of both the elements and the visitors.

Java example

[edit]

The following example is in the Java programming language, and shows how the contents of a tree of nodes (in this case describing the components of a car) can be printed. Instead of creating "print" methods for each subclass (Wheel, Engine, Body, and Car), a single class (CarElementPrintVisitor) performs the required printing action. Because different subclasses

open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

require slightly different actions to print properly, CarElementDoVisitor dispatches actions based on the class of the argument passed to it.

Diagram

[edit]

Sources

[edit]

interface ICarElementVisitor { void visit(Wheel wheel); void visit(Engine engine); void visit(Body body); void visit(Car car); }

open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

interface ICarElement { void accept(ICarElementVisitor visitor); // CarElements have to provide accept(). } class Wheel implements ICarElement { private String name; public Wheel(String name) { this.name = name; } public String getName() { return this.name; } public void accept(ICarElementVisitor visitor) { /* * accept(ICarElementVisitor) in Wheel implements * accept(ICarElementVisitor) in ICarElement, so the call * to accept is bound at run time. This can be considered * the first dispatch. However, the decision to call * visit(Wheel) (as opposed to visit(Engine) etc.) can be * made during compile time since 'this' is known at compile * time to be a Wheel. Moreover, each implementation of * ICarElementVisitor implements the visit(Wheel), which is * another decision that is made at run time. This can be * considered the second dispatch. */ visitor.visit(this); } } class Engine implements ICarElement { public void accept(ICarElementVisitor visitor) { visitor.visit(this); } } class Body implements ICarElement { public void accept(ICarElementVisitor visitor) { visitor.visit(this); }

open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

} class Car implements ICarElement { ICarElement[] elements; public Car() { //create new Array of elements this.elements = new ICarElement[] { new Wheel("front left"), new Wheel("front right"), new Wheel("back left") , new Wheel("back right"), new Body(), new Engine() }; } public void accept(ICarElementVisitor visitor) { for(ICarElement elem : elements) { elem.accept(visitor); } visitor.visit(this); } } class CarElementPrintVisitor implements ICarElementVisitor { public void visit(Wheel wheel) { System.out.println("Visiting " + wheel.getName() + " wheel"); } public void visit(Engine engine) { System.out.println("Visiting engine"); } public void visit(Body body) { System.out.println("Visiting body"); } public void visit(Car car) { System.out.println("Visiting car"); } } class CarElementDoVisitor implements ICarElementVisitor { public void visit(Wheel wheel) { System.out.println("Kicking my " + wheel.getName() + " wheel"); }

open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

public void visit(Engine engine) { System.out.println("Starting my engine"); } public void visit(Body body) { System.out.println("Moving my body"); } public void visit(Car car) { System.out.println("Starting my car"); } } public class VisitorDemo { public static void main(String[] args) { ICarElement car = new Car(); car.accept(new CarElementPrintVisitor()); car.accept(new CarElementDoVisitor()); } } Note : A more flexible approach to this pattern is to create a wrapper class implementing the interface defining the accept method. The wrapper contains a reference pointing to the ICarElement which could be initialized through the constructor. This approach avoids having to implement an interface on each element. [see article Java Tip 98 article below]

Output

[edit]

Visiting front left wheel Visiting front right wheel Visiting back left wheel Visiting back right wheel Visiting body Visiting engine Visiting car Kicking my front left wheel Kicking my front right wheel Kicking my back left wheel Kicking my back right wheel Moving my body Starting my engine

open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

Starting my car

Lisp Example
Sources
[edit]

[edit]

(defclass auto () ((elements :initarg :elements))) (defclass auto-part () ((name :initarg :name :initform "<unnamed-car-part>"))) (defmethod print-object ((p auto-part) stream) (print-object (slot-value p 'name) stream)) (defclass wheel (auto-part) ()) (defclass body (auto-part) ()) (defclass engine (auto-part) ()) (defgeneric traverse (function object other-object)) (defmethod traverse (function (a auto) other-object) (with-slots (elements) a (dolist (e elements) (funcall function e other-object)))) ;; do-something visitations ;; catch all (defmethod do-something (object other-object) (format t "don't know how ~s and ~s should interact~%" object other-object)) ;; visitation involving wheel and integer (defmethod do-something ((object wheel) (other-object integer)) (format t "kicking wheel ~s ~s times~%" object other-object)) ;; visitation involving wheel and symbol (defmethod do-something ((object wheel) (other-object symbol)) (format t "kicking wheel ~s symbolically using symbol ~s~%" object other-object))

open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

(defmethod do-something ((object engine) (other-object integer)) (format t "starting engine ~s ~s times~%" object other-object)) (defmethod do-something ((object engine) (other-object symbol)) (format t "starting engine ~s symbolically using symbol ~s~%" object other-object)) (let ((a (make-instance 'auto :elements `(,(make-instance 'wheel :name "front-left-wheel") ,(make-instance 'wheel :name "front-right-wheel") ,(make-instance 'wheel :name "rear-right-wheel") ,(make-instance 'wheel :name "rear-right-wheel") ,(make-instance 'body :name "body") ,(make-instance 'engine :name "engine"))))) ;; traverse to print elements ;; stream *standard-output* plays the role of other-object here (traverse #'print a *standard-output*) (terpri) ;; print newline ;; traverse with arbitrary context from other object (traverse #'do-something a 42) ;; traverse with arbitrary context from other object (traverse #'do-something a 'abc))

Output

[edit]

"front-left-wheel" "front-right-wheel" "rear-right-wheel" "rear-right-wheel" "body" "engine" kicking wheel "front-left-wheel" 42 times kicking wheel "front-right-wheel" 42 times kicking wheel "rear-right-wheel" 42 times kicking wheel "rear-right-wheel" 42 times don't know how "body" and 42 should interact starting engine "engine" 42 times kicking wheel "front-left-wheel" symbolically using symbol ABC

open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

kicking wheel "front-right-wheel" symbolically using symbol ABC kicking wheel "rear-right-wheel" symbolically using symbol ABC kicking wheel "rear-right-wheel" symbolically using symbol ABC don't know how "body" and ABC should interact starting engine "engine" symbolically using symbol ABC

Notes

[edit]

The other-object parameter is superfluous in traverse. The reason is that it is possible to use an anonymous function which calls the desired target method with a lexically captured object: (defmethod traverse (function (a auto)) ;; other-object removed (with-slots (elements) a (dolist (e elements) (funcall function e)))) ;; from here too ;; ... ;; alternative way to print-traverse (traverse (lambda (o) (print o *standard-output*)) a) ;; alternative way to do-something with ;; elements of a and integer 42 (traverse (lambda (o) (do-something o 42)) a) Now, the multiple dispatch occurs in the call issued from the body of the anonymous function, and so traverse is just a mapping function which distributes a function application over the elements of an object. Thus all traces of the Visitor Pattern disappear, except for the mapping function, in which there is no evidence of two objects being involved. All knowledge of there being two objects and a dispatch on their types is in the lambda function.

Scala Example
Sources
[edit]

[edit]

class Date(val day: Int, val month: Int, val year: Int) abstract class CarElement { var lastServiced = new Date(0, 0, 0)

open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

} class Wheel(val name: String) extends CarElement {} class Body extends CarElement {} class Engine extends CarElement {} class Car extends CarElement { val elements: List[CarElement] = List(new Wheel("front left"), new Wheel("front right"), new Wheel("back left"), new Wheel("back right"), new Body(), new Engine()) } object CarPartProcessors { def printCarElement(element: CarElement): Unit = { element match { case wheel: Wheel => println("Visiting " + wheel.name + " wheel") case engine: Engine => println("Visiting engine") case body: Body => println("Visiting body") case car: Car => { car.elements.map(printCarElement) println("Visting car") } case other => println("Visting unknown CarElement " + other) } } def interactWithCarElement(element: CarElement): Unit = { element match { case wheel: Wheel => println("Kicking my " + wheel.name + " wheel") case engine: Engine => println("Starting my engine") case body: Body => println("Moving my body") case car: Car => { car.elements.map(interactWithCarElement) println("Starting my car") } case other => println("Unknown CarElement " + other + " no interaction defined") } } } object example extends App { val car = new Car() CarPartProcessors.printCarElement(car) CarPartProcessors.interactWithCarElement(car)

open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

Output

[edit]

Visiting front left wheel Visiting front right wheel Visiting back left wheel Visiting back right wheel Visiting body Visiting engine Visiting car Kicking my front left wheel Kicking my front right wheel Kicking my back left wheel Kicking my back right wheel Moving my body Starting my engine Starting my car

Notes

[edit]

The Scala implementation has neither the notion of a visitor interface nor a visitable interface. Now, suppose we add the following class definition: class Transmission(val kind: String) extends CarElement { override def toString = { kind + " Transmission" } } and change the definition of the Car class to: class Car extends CarElement { val elements: List[CarElement] = List(new Wheel("front left"), new Wheel("front right"), new Wheel("back left"), new Wheel("back right"), new Body(), new Engine(), new Transmission("Manual")) } Upon rerunning the program, we get:

open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

Output

[edit]

Visiting front left wheel Visiting front right wheel Visiting back left wheel Visiting back right wheel Visiting body Visiting engine Visting unknown CarElement Manual Transmission Visting car Kicking my front left wheel Kicking my front right wheel Kicking my back left wheel Kicking my back right wheel Moving my body Starting my engine Unknown CarElement Manual Transmission no interaction defined Starting my car

State

[edit] This section does not cite any references or sources. Please help improve this section by adding citations to reliable sources. Unsourced material may be challenged and removed. (January 2011)

Aside from potentially improving separation of concerns, the visitor pattern has an additional advantage over simply calling a polymorphic method: a visitor object can have state. This is extremely useful in many cases where the action performed on the object depends on previous such actions. An example of this is a pretty-printer in a programming language implementation (such as a compiler or interpreter). Such a pretty-printer object (implemented as a visitor, in this example), will visit nodes in a data structure that represents a parsed and processed program. The pretty-printer will then generate a textual representation of the program tree. To make the representation human-readable, the pretty-printer should properly indent program statements and expressions. The current indentation level can then be tracked by the visitor as its state, correctly applying encapsulation, whereas in a simple polymorphic method invocation, the indentation level would have to be exposed as a parameter and the caller would rely on the method implementation to use and propagate this parameter correctly.

open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

Related design patterns

[edit]

Command pattern: It encapsulates like the visitor pattern one or more functions in an object to present them to a caller. Unlike the visitor, the command pattern does not enclose a principle to traverse the object structure. Iterator pattern: This pattern defines a traversal principle like the visitor pattern without making a type differentiation within the traversed objects.

See also

[edit]

Double and multiple dispatch Hierarchical visitor pattern Function object Algebraic data type

References External links

[edit]

[edit]
Wikimedia Commons has media related to Visitor pattern. The Wikibook Computer Science Design Patterns has a page on the topic of: Visitor implementations in various languages The Wikibook Computer Science/Design Patterns has a page on the topic of: Visitor implementations in various languages

The Visitor Family of Design Patterns by Robert C. Martin - a rough chapter from The Principles, Patterns, and Practices of Agile Software Development , Robert C. Martin, Prentice Hall Visitor pattern in UML and in LePUS3 (a Design Description Language) Article "Componentization: the Visitor Example by Bertrand Meyer and Karine Arnout, Computer (IEEE), vol. 39, no. 7, July 2006, pages 23-30. Article A Type-theoretic Reconstruction of the Visitor Pattern Article "The Essence of the Visitor Pattern " by Jens Palsberg and C. Barry Jay. 1997 IEEE-CS COMPSAC paper showing that accept() methods are unnecessary when reflection is available; introduces term 'Walkabout' for the technique. Article "A Time for Reflection " by Bruce Wallace - subtitled "Java 1.2's reflection capabilities eliminate burdensome accept() methods from your Visitor pattern" Visitor Patterns as a universal model of terminating computation.

open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

Visitor Pattern

using reflection(java). , Provides a context-free and type-safe implementation of the Visitor Pattern in

PerfectJPattern Open Source Project Java based on Delegates. Visitor Design Pattern

Article Java Tip 98: Reflect on the Visitor design pattern


V T E Creational GoF Patterns Structural Behavioral Concurrency Patterns Architectural Patterns Other Patterns Books People Com m unities

Design Patterns
Abstract factory Builder Factory method Prototype Singleton Adapter Bridge Composite Decorator Facade Flyw eight Proxy

[hide]

Chain of responsibility Command Interpreter Iterator Mediator Memento Observer State Strategy Template method Visitor

Active Object Balking Double-checked locking Event-based Asynchronous Guarded suspension Join Lock Monitor Proactor Reactor Read w rite lock Scheduler Thread pool Thread-Specific Storage Front controller Interceptor MVC n-tier Specification Publish-subscribe Naked objects Service Locator Active record Identity map Data access object Data transfer object Dependency injection Lazy loading Mock object Null object Object pool Servant Type Tunnel Design Patterns Enterprise Integration Patterns Christopher Alexander Erich Gamma Ralph Johnson John Vlissides Grady Booch Kent Beck Ward Cunningham Martin Fow ler Robert Martin Jim Coplien Douglas Schmidt Linda Rising The Hillside Group The Portland Pattern Repository

Categories: Software design patterns

This page w as last modified on 29 November 2013 at 07:19. Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Terms of Use and Privacy Policy. Wikipedia is a registered trademark of the Wikimedia Foundation, Inc., a non-profit organization. Privacy policy About Wikipedia Disclaimers Contact Wikipedia Developers Mobile view

open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

Вам также может понравиться