views:

432

answers:

9

I always thought an object needs the data and the messages to act on it. When would you want a method that is extrinsic to the object? What rule of thumb do you follow to have a visitor? This is supposing that you have full control of the object graph.

+2  A: 

I would always recommend using a visitor when you have full knowledge of what classes that implement an interface. In this way you won't do any not-so-pretty instanceof-calls, and the code becomes a lot more readable. Also, once a visitor has been implemented can be reused in many places, present and future.

Lars Andren
+2  A: 

Sometimes its just a matter of organization. If you have n-kinds of objects (ie: classes) with m-kinds of operations (ie: methods), do you want to have the n * m class/method pairs to be grouped by class or by method? Most OO languages strongly lean towards having things grouped by class, but there are cases where organizing by operation makes more sense. For example, in a multi-phase processing of object graphs, like in a compiler, is often more useful to think about each phase (ie: operation) as a unit rather than to think about all of operations that can happen to a particular sort of node.

A common use-case for the Visitor pattern where it's more than just strictly organizational is to break unwanted dependencies. For example, it's generally undesirable to have your "data" objects depend on your presentation layer, especially if you imagine that you may have multiple presentation layers. By using the visitor pattern, details of the presentation layer live in the visitor objects, not in methods of the data objects. The data objects themselves only know about the abstract visitor interface.

Laurence Gonsalves
+7  A: 

The visitor pattern is particularly useful when applying an operation to all elements of a fairly complicated data structure for which traversal is non-trivial (e.g. traversing over the elements in parallel, or traversing a highly interconnected data structure) or in implementing double-dispatch. If the elements are to be processed sequentially and if double-dispatch is not needed, then implementing a custom Iterable and Iterator is usually the better choice, especially since it fits in better with the other APIs.

Michael Aaron Safyan
This answer is completely correct, but probably greek to someone who isn't really familiar with the visitor pattern and how it's used.
RMorrisey
As the previous comment said, it's technically correct but difficult to digest and understand. Do you have a practical example of this case that illustrates the point? I always find that abstract definitions of patterns without examples make it very unlikely that you're writing code and recognise the use-case like 'ah ha this is where I use pattern x"
Alb
+3  A: 

I use it a lot when I find I want to put a method that will be stateful onto Entity/DataObject/BusinessObject but I really don't want to introduce that statefulness to my object. A stateful visitor can do the job, or generate a collection of stateful executor objects from my non-stateful data objects. Particularly useful when processing of the work is going to be farmed out to executor threads, many stateful visitor/workers can reference the same group of non-stateful objects.

Affe
+3  A: 

For me, the only one reason to use visitor pattern is when I need to perform double dispatch on graph-like data structure like tree/trie.

monn
Could you explain your answer a little more for a newbie like me? Thanks.
kunjaan
+2  A: 

I always thought an object needs the data and the messages to act on it. When would you want a method that is extrinsic to the object? What rule of thumb do you follow to have a visitor? This is supposing that you have full control of the object graph.

It's sometimes not convenient to have all behaviors for a particular object defined in one class. For instance in Java, if your module requires a method toXml to be implemented in a bunch of classes originally defined in another module, it's complicated because you can not write toXml somewhere else than the original class file, which mean you can not extend the system without changing existing sources (in Smalltalk or other languages, you can group method in extension which are not tied to a particular file).

More generally, there's a tension in statically typed languages between the ability to (1) add new functions to existing data types, and (2) add new data types implementations supporting the same functions -- that's called the expression problem (wikipedia page).

Object oriented languages excel at point 2. If you have an interface, you can add new implementations safely and easily. Functional languages excel at point 1. They rely on pattern matching/ad-hoc polymorphism/overloading so you can add new functions to existing types easily.

The visitor pattern is a way to support point 1 in an object-oriented design: you can easily extend the system with new behaviors in a type-safe way (which wouldn't be the case if you do kind of manual pattern matching with if-else-instanceof because the language would never warn you if a case is not covered).

Visitors are then typically used when there is a fixed set of known types, which I think is what you meant by "full control of the object graph". Examples include token in a parser, tree with various types of nodes, and similar situations.

So to conclude, I would say you were right in your analysis :)

PS: The visitor pattern works well with the composite pattern, but they are also useful individually

ewernli
+2  A: 

When you have the following 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.

Then you can use a Visitor pattern with one of the following intents:

  • Represent an operation to be performed on the elements of an object structure.
  • Define a new operation without changing the classes of the elements on which it operates.
  • The classic technique for recovering lost type information.
  • Do the right thing based on the type of two objects.
  • Double dispatch

(From http://sourcemaking.com/design_patterns/visitor)

TomWij
+1  A: 

The visitor pattern is most useful when you need behaviour to vary by object type (in a class hierarchy), and that behaviour can be defined in terms of the public interface provided by the object. The behaviour is not intrinsic to that object and doesn't benefit from or require encapsulation by the object.

I find visitors often naturally arises with graphs/trees of objects, where each node is part of a class hierarchy. To allow clients to walk the graph/tree and handle any each type of node in a uniform way, the Visitor pattern is really the simplest alternative.

For example, consider an XML DOM - a Node is the base class, with Element, Attribute and other types of Node define the class hierarchy.

Imagine the requirement is to output the DOM as JSON. The behaviour is not intrinsic to a Node - if it were, we would have to add methods to Node to handle all formats that the client might need (toJSON(), toASN1(), toFastInfoSet() etc.) We could even argue that toXML() doesn't belong there, although that might be provided for convenience since it is going to be used by most clients, and is conceptually "closer" to the DOM, so toXML could be made intrinsic to Node for convenience - although it doesn't have to be, and could be handled like all the other formats.

As Node and it's subclasses make their state fully available as methods, we have all the information needed externally to be able to convert the DOM to some output format. Instead of than putting the output methods on the Node object, we can use a Visitor interface, with an abstract accept() method on Node, and implementation in each subclass.

The implementation of each visitor method handles the formatting for each node type. It can do this because all the state needed is available from the methods of each node type.

By using a visitor, we open the door to implementing any output format desired without needing to burden each Node class with that functionality.

mdma
A: 

Visitor pattern is a very natural solution to double dispatch problems. Double dispatch problem is a subset of dynamic dispatch problems and it stems from the fact that method overloads are determined statically at compile time, unlike virtual(overriden) methods, which are determined at runtime.

Consider this scenario:

public class CarOperations {
  void doCollision(Car car){}
  void doCollision(Bmw car){}
}

public class Car {
  public void doVroom(){}
}      

public class Bmw extends Car {
  public void doVroom(){}
}

public static void Main() {
    Car bmw = new Bmw();

    bmw.doVroom(); //calls Bmw.doVroom() - single dispatch, works out that car is actually Bmw at runtime.

    CarOperations carops = new CarOperations();
    carops.doCollision(bmw); //calls CarOperations.doCollision(Car car) because compiler chose doCollision overload based on the declared type of bmw variable
}

This code below is adopted from my previous answer and translated to Java. The problem is somewhat different from the above sample, but demonstrates the essence of Visitor pattern.

//This is the car operations interface. It knows about all the different kinds of cars it supports
//and is statically typed to accept only certain ICar subclasses as parameters
public interface CarVisitor {
   void StickAccelerator(Toyota car);
   void ChargeCreditCardEveryTimeCigaretteLighterIsUsed(Bmw car);
}

//Car interface, a car specific operation is invoked by calling PerformOperation  
public interface Car {

   public string getMake();
   public void setMake(string make);

   public void performOperation(CarVisitor visitor);
}

public class Toyota implements Car {
   private string make;
   public string getMake() {return this.make;}
   public void setMake(string make) {this.make = make;}

   public void performOperation(CarVisitor visitor) {
     visitor.StickAccelerator(this);
   }
}

public class Bmw implements Car{
   private string make;
   public string getMake() {return this.make;}
   public void setMake(string make) {this.make = make;}

   public void performOperation(ICarVisitor visitor) {
     visitor.ChargeCreditCardEveryTimeCigaretteLighterIsUsed(this);
   }
}

public class Program {
  public static void Main() {
    Car car = carDealer.getCarByPlateNumber("4SHIZL");
    CarVisitor visitor = new SomeCarVisitor();
    car.performOperation(visitor);
  }
}
Igor Zevaka