What is the Visitor Pattern?
Learn the Visitor design pattern — double dispatch, accept/visit and its trade-offs — with a Java shape hierarchy example.
Expected Interview Answer
The Visitor pattern is a behavioral design pattern that lets you add new operations over a fixed set of related classes without modifying those classes, by moving the operation into a separate visitor object and using double dispatch to select the correct behavior for each concrete type.
Each element class implements an accept(Visitor v) method that calls back into v.visit(this), and each visitor implements an overloaded visit method per concrete element type. This "double dispatch" resolves both the element’s runtime type and the visitor’s runtime type together, something single dispatch (a plain overridden method) cannot do. It is well suited when the class hierarchy is stable but new operations are added frequently — the trade-off is the reverse of a typical hierarchy: adding a new element type requires updating every visitor, while adding a new operation requires only a new visitor class. This makes Visitor a good fit for compilers walking an abstract syntax tree, but a poor fit for hierarchies that change shape often.
- Adds new operations without touching existing element classes
- Keeps related operation logic grouped together in one visitor class
- Enables double dispatch for type-specific behavior across two hierarchies
- Well suited to stable structures with frequently changing operations
AI Mentor Explanation
A single stadium inspector "visits" every facility — pitch, pavilion, scoreboard, nets — and each facility knows how to present itself to the inspector (accept), while the inspector carries the specific checklist logic (visit) for pitches versus pavilions versus scoreboards. Adding a new inspection type (safety, hygiene) just means writing a new inspector, without altering the facilities themselves. That is the Visitor pattern: the operation lives in the visiting object, and each element merely accepts the visitor and lets it decide what to do based on the element’s actual type.
Step-by-Step Explanation
Step 1
Define the Visitor interface
Declare an overloaded visit() method for each concrete element type.
Step 2
Add accept() to each element
Every element class implements accept(Visitor v) that calls v.visit(this).
Step 3
Implement concrete visitors
Each operation becomes its own class implementing all visit() overloads.
Step 4
Traverse and dispatch
Calling element.accept(visitor) triggers double dispatch to the right visit() overload.
What Interviewer Expects
- A correct explanation of double dispatch and why it’s needed
- Awareness of the trade-off: easy to add operations, hard to add element types
- A realistic use case (AST traversal, document processing)
- Understanding that accept()/visit() is the mechanical core of the pattern
Common Mistakes
- Confusing Visitor with a simple loop calling instance methods (missing double dispatch)
- Not recognizing that adding a new element type breaks every existing visitor
- Forgetting the accept() method is required on every element class
- Applying Visitor to a frequently-changing hierarchy, where it adds unnecessary churn
Best Answer (HR Friendly)
“The Visitor pattern lets you add new operations to a group of related classes without changing those classes themselves. Each class has a small accept() method that hands itself to a visitor object, and the visitor decides what to do based on the actual type through double dispatch. It’s great when the class hierarchy is stable but you keep adding new operations, like a compiler running different passes over the same syntax tree.”
Code Example
interface ShapeVisitor {
void visit(Circle c);
void visit(Square s);
}
interface Shape {
void accept(ShapeVisitor visitor);
}
class Circle implements Shape {
double radius;
Circle(double radius) { this.radius = radius; }
public void accept(ShapeVisitor visitor) { visitor.visit(this); }
}
class Square implements Shape {
double side;
Square(double side) { this.side = side; }
public void accept(ShapeVisitor visitor) { visitor.visit(this); }
}
class AreaVisitor implements ShapeVisitor {
public void visit(Circle c) { System.out.println(Math.PI * c.radius * c.radius); }
public void visit(Square s) { System.out.println(s.side * s.side); }
}Follow-up Questions
- What is double dispatch and why does Visitor need it?
- What happens when a new element type is added to the hierarchy?
- When would you avoid using the Visitor pattern?
- How does Visitor relate to compiler AST traversal?
MCQ Practice
1. The Visitor pattern relies on which mechanism to resolve the correct behavior?
accept() calling back into an overloaded visit() resolves both the element and visitor types together.
2. Adding a new element type to a Visitor-based hierarchy requires?
Every visitor’s interface must gain a new visit() overload, making element additions the costly direction.
3. Visitor is best suited to hierarchies that are?
Visitor shines when the type hierarchy rarely changes but new cross-cutting operations are added often.
Flash Cards
Visitor pattern in one line? — External visitor objects add new operations to a class hierarchy via double dispatch.
Key mechanism? — accept(visitor) calls visitor.visit(this), resolving both types together.
Main trade-off? — Easy to add operations; hard to add new element types (every visitor must update).
Classic use case? — Compiler passes walking an abstract syntax tree.