The Visitor Pattern: Separating Algorithms from Object Structures

2026-05-05

You have a stable hierarchy of types — AST nodes, file system entries, shape primitives — and you keep needing to add new operations across all of them. Every new operation forces you to crack open every class and add another method. The Visitor pattern flips this: instead of putting operations on the types, you put the type-dispatch on the operation.

The mechanic is double dispatch. Each node exposes a single accept(visitor) method that calls back visitor.visitConcreteType(this). The runtime type of the node selects the accept implementation; the visitor's method overload selects the operation. Two dispatches, one for each axis.

Concrete example — a small expression evaluator:

This is the core trade-off, often called the Expression Problem:

Rule of thumb: if your type hierarchy changes more than ~2× per year but operations rarely change, skip Visitor and use polymorphic methods. If the hierarchy is stable (think: language grammars, document formats, geometric primitives) but you keep inventing new things to do with it, Visitor pays for itself after roughly 3 operations × 4 types — about 12 dispatch sites.

Real-world sightings: Roslyn's C# compiler uses CSharpSyntaxVisitor<T> for analyzers and refactorings. Babel's AST traversal is essentially Visitor. ESLint rules are Visitors over the JS AST.

Practical pitfalls:

See it in action: Check out Visitor - Design Patterns in 5 minutes by levonog to see this theory applied.
Key Takeaway: Use Visitor when your type hierarchy is stable but the operations on it keep multiplying — it trades easy operation-addition for hard type-addition.

All newsletters