Visitor (Behavioral)
Intent / problem it solves
Represent an operation to apply to elements of an object structure without changing their classes. Lets you add new operations without modifying every element type (trade-off: element types must accept visitors).
When to use / when NOT
Use for AST walkers, document export (PDF/HTML), or complex traversals with many operations.
Avoid when the structure churns often—visitor coupling can hurt; pattern matching on sealed types can replace it in modern languages.
Structure
Visitor declares visitConcrete per element; elements expose accept(visitor); double dispatch routes calls.
Go example
package main
import "fmt"
type Visitor interface {
VisitSquare(*Square)
}
type Shape interface {
Accept(Visitor)
}
type Square struct{ Side int }
func (square *Square) Accept(visitor Visitor) { visitor.VisitSquare(square) }
type AreaVisitor struct{}
func (AreaVisitor) VisitSquare(square *Square) {
fmt.Println(square.Side * square.Side)
}
func main() {
var shape Shape = &Square{Side: 4}
shape.Accept(AreaVisitor{})
}JavaScript example
class XmlExporter {
visitHeading(node) {
return `<h1>${node.text}</h1>`;
}
visitParagraph(node) {
return `<p>${node.text}</p>`;
}
}
class HeadingNode {
constructor(text) {
this.text = text;
}
accept(visitor) {
return visitor.visitHeading(this);
}
}
class ParagraphNode {
constructor(text) {
this.text = text;
}
accept(visitor) {
return visitor.visitParagraph(this);
}
}
const exporter = new XmlExporter();
const doc = [
new HeadingNode('Notes'),
new ParagraphNode('hello'),
];
console.log(doc.map((node) => node.accept(exporter)).join(''));Interview phrase
“Visitor centralizes cross-cutting operations over a stable hierarchy—great for compilers; painful when new node types appear weekly.”
Related LLD case studies
Map to LLD for AST evaluation, document export, or pricing rules across cart line types in LLD case studies.
Last updated on
Spotted something unclear or wrong on this page?