| Visitor Pattern |
Article Index for Visitor |
Website Links For Visitor |
Information AboutVisitor Pattern |
| CATEGORIES ABOUT VISITOR PATTERN | |
| software design patterns | |
| articles with example java code | |
|
The idea is to use a structure of element classes, each of which has an accept method that takes a visitor object as an argument. Visitor is an Interface that has a visit() method for each element class. The accept() method of an element class calls back the visit() method for its class. Separate concrete visitor classes can then be written that perform some particular operations.One of these visit() methods of a concrete visitor can be thought of as methods not of a single class, but rather methods of a pair of classes: the concrete visitor and the particular element class. Thus the visitor pattern simulates Double Dispatch in a conventional single-dispatch Object-oriented language such as Java , Smalltalk , and C++ . For an explanation of how double dispatch differs from Function Overloading , see Double Dispatch Is More Than Function Overloading in the double dispatch article. In the Java language, two techniques have been documented which use Reflection to simplify the mechanics of double dispatch simulation in the visitor pattern: getting rid of accept() methods , and getting rid of extra visit() methods .The visitor pattern also specifies how iteration occurs over the object structure. In the simplest version, where each algorithm needs to iterate in the same way, the accept() method of a container element, in addition to calling back the visit() method of the visitor, also passes the visitor object to the accept() method of all its constituent child elements.Because the Visitor object has one principal function (manifested in a plurality of specialized methods) and that function is called visit(), the Visitor can be readily identified as a potential Function Object or Functor . Likewise, the accept() function can be identified as a function applicator, a mapper, which knows how to traverse a particular type of object and apply a function to its elements. The Common Lisp example found on the Visitor Pattern Examples page shows an implementation of the same behavior as the other examples, using functions. It's noteworthy that although Common Lisp has one of the few object systems that support Multiple Dispatch , the first part of the example does not rely on multiple dispatch. The use of multiple dispatch is introduced in a second example; yet the framework remains the same. Lisp's object system with its multiple dispatch does not replace the Visitor pattern, but merely provides a more concise implementation of it in which the pattern all but disappears.EXAMPLE The following example is an example in the Java Programming Language : interface Visitor { void visit(Wheel wheel); void visit(Engine engine); void visit(Body body); void visit(Car car); } interface Visitable { void accept(Visitor visitor); } class Wheel implements Visitable { private String name; Wheel(String name) { this.name = name; } String getName() { return this.name; } public void accept(Visitor visitor) { visitor.visit(this); } } class Engine implements Visitable{ public void accept(Visitor visitor) { visitor.visit(this); } } class Body implements Visitable{ public void accept(Visitor visitor) { visitor.visit(this); } } class Car implements Visitable { private Engine engine = new Engine(); private Body body = new Body(); private Wheel {Link without Title} wheels = { new Wheel("front left"), new Wheel("front right"), new Wheel("back left") , new Wheel("back right") }; public Engine getEngine() { return this.engine; } public Body getBody() { return this.body; } public Wheel {Link without Title} getWheels() { return this.wheels; } public void accept(Visitor visitor) { visitor.visit(this); engine.accept(visitor); body.accept(visitor); for(int i = 0; i < wheels.length; i++) { Wheel wheel = wheels {Link without Title} ; wheel.accept(visitor); } } } class PrintVisitor implements Visitor { 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 DoVisitor implements Visitor { public void visit(Wheel wheel) { System.out.println("Steering my wheel"); } 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("Vroom!"); } } public class VisitorDemo { static public void main(String {Link without Title} args){ Car car = new Car(); Visitor visitor = new PrintVisitor(); Visitor doVisitor = new DoVisitor(); car.accept(visitor); car.accept(doVisitor); } } STATE 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. In order 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. SEE ALSO EXTERNAL LINKS
|
|
|