Problem

Collections are data types widely used in object oriented programming. They often contain objects of varying types and operations will need to be performed on all the elements in a collection without prior knowledge of the types.

Solution

The visitor pattern represents an operation to be performed on each element of a structure. We define a new operation without changing the classes of the elements by defining visit() and accept() methods.

Related Patterns

Discussion

The main purpose of the visitor pattern is to abstract away functionality that can be applied to any number of objects within a hierarchy (collection) of objects. This encourages building lightweight element classes because processing is no longer among the list of responsibilities. New functionality can easily be added to the original inheritance hierarchy by simply adding a new Visitor subclass.
The visitor pattern is generally not a good match if the object hierarchy is unstable or if the public interface for the candidate objects is insufficient for the access that Visitor classes will require.

Examples

We use the Visitor pattern to represent an operation to be performed on elements of an object structure without chaning the classes of objects upon which it operates. A real world example would be the operation of a pizza delivery company. When a person calls the restaurant, the operator on the phone places the order for them. Other employees begin making the pizza. The task of making food visits several other people on the way to the customer, namely the delivery driver.

Code

This is the beginnings of a Visitor framework for the parsing of a arithmetic expression parse tree.

  public abstract class Node
  {
    public abstract Double eval();
  }
  public class OpNode extends Node
  {
    public Node left, right;
    private String operator;
    <T, K> T accept(NodeVisitor<T, K> visitor, K k);
  }
  public class Leaf extends Node
  {
    public Double value;
    public Double eval(){
      return value;
    }
    <T, K> T accept(NodeVisitor<T, K> visitor, K k);
  }

  public interface NodeVisitor<T, K>{ // here we use generics
    T visitLeaf(Leaf leaf, K k);
    T visitOpNode(OpNode node, K k);
  }
  public class EvalVisitor implements NodeVisitor<Double, Void>{
    public Double visitNumber(Leaf leaf, Void p){
      return leaf.eval();
    }
    public Double visitOpNode(OpNode opnode, Void p){
      switch(opnode.getOperator()){
        case '+': return opnode.left.accept(this, p) + opnode.right.accept(this, p);
        case '-': return opnode.left.accept(this, p) - opnode.right.accept(this, p);
        case '*': return opnode.left.accept(this, p) * opnode.right.accept(this, p);
        case '/': return opnode.left.accept(this, p) / opnode.right.accept(this, p);
      }
    }
  }