Visitor Pattern

The Visitor pattern is useful in designing an operation (method or algorithm) across a collection of objects or a class hierarchy. The Visitor pattern allows the operation to be defined without changing the classes of the collection (keeping the hierarchy clean).
To accomplish this, the Visitor pattern suggests defining the operation in a separate class referred to as a visitor class. For every new operation to be defined, a new visitor class is created.

Implementation

Usually visitor pattern is implemented this way:

1- Every visitor class that operates on objects of the same set of classes can be designed to implement a corresponding VisitorInterface interface. A typical VisitorInterface declares a set of visit(Type1 t1) methods, one for each object type from the object collection.

2- Classes from the object collection need to define a method: accept(VisitorInterface vi) to be visitable.

3- Whenever a new operation is to be defined across the object collection, a new visitor class needs to be created with implementation for the new operation. The visitor class needs to implement all of the visit(ObjectType) methods declared in the VisitorInterface interface to process different types of objects. With this design, defining a new operation does not require any changes to the classes of the object collection.

4- Whenever a new type of object is to be added to the object collection and is to be visitable, the class of the object must provide a method similar to accept(Visitor) and as part of this method implementation it should invoke the visit(ObjectType) method on the visitor object passing itself as an argument to it. Also a corresponding visit(ObjectType) method needs to be added to the VisitorInterface interface and needs to be implemented by all the concrete visitor classes.

Structure

visitori.jpeg

Example

See this more real example:

visitor2.jpeg

Another example:

// the class hierarchy
public interface Order {
    public int priceCalculator(OrderCalculator c);
}
 
public class HardwareOrder implements Order{
 
    @Override
    public int priceCalculator(OrderCalculator c) {
        return c.calculatePrice(this);
    }
}
 
public class SoftwareOrder implements Order{
 
    @Override
    public int priceCalculator(OrderCalculator c) {
        return c.calculatePrice(this);
    }
}
 
//algo
public interface OrderCalculator {
    int calculatePrice(Order order);
}
 
public class CalculateHardwarePrice  implements OrderCalculator{
 
    @Override
    public int calculatePrice(Order order) {
        //algorithm for calculating hardware order price
        //this algo is separate from the order hierarchy 
        return ...
    }
}

Consequences

  • Iterator operates on a set of objects that have a common parent but visitor does not have this restriction.

Implementation

  • We can put responsibility for traversal in either: the object structure, in the visitor, or in a separate iterator object - see Iterator pattern. Often the object structure is responsible for iteration. A collection will simply iterate over its elements, calling the "accept" method on each.

Related Patterns

  • Composite: Visitors can be used to apply an operation over an object structure defined by the Composite pattern.
  • Interpreter: Visitor may be applied to do the interpretation.
  • Iterator
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License