Visitor Design Pattern Explained
Visitor lets you add new operations to a group of related classes without modifying those classes themselves, by moving the operation into a separate visitor...
Published November 13, 2026
The Visitor pattern lets you add new operations to a set of classes without modifying them, by moving the operation into a separate visitor object that each element 'accepts'.
The concept
Each element has accept(visitor) that calls visitor.visitX(this). The visitor contains one method per element type.
It suits stable class hierarchies with many changing operations, such as ASTs in compilers.
Double dispatch
- Client calls node.accept(visitor)
- Node calls visitor.visitNode(node)
- Visitor runs logic for that type
- New operation = new visitor, no node changes
Practical examples
AST nodes
class Num { constructor(v) { this.v = v } accept(vis) { return vis.num(this) } }
class Add { constructor(l, r) { this.l = l; this.r = r } accept(vis) { return vis.add(this) } }Evaluator visitor
const evalV = { num: (n) => n.v, add: (n) => n.l.accept(evalV) + n.r.accept(evalV) }
new Add(new Num(2), new Num(3)).accept(evalV) // 5Printer visitor
const show = { num: (n) => `${n.v}`, add: (n) => `(${n.l.accept(show)} + ${n.r.accept(show)})` }Common mistakes
- Using it when the element classes change often.
- Breaking encapsulation by exposing internals.
- Forgetting every visitor needs a method per type.
Key takeaways
- Add operations without touching classes.
- Double dispatch via accept().
- Best for stable hierarchies.
FAQ
Visitor vs Strategy?
Visitor varies by element type; Strategy varies one algorithm.
Where used?
Compilers, linters, serialisers.