Skip to content

Visitor

Visitor lets you define a new operation over a structure of objects without modifying the classes that make up that structure. You package the operation into a visitor and let each element accept it.

Suppose you have a hierarchy of shapes — circles, rectangles, and so on — and you keep needing new operations across all of them: compute area, render to SVG, serialize to JSON, estimate paint cost. If each operation lives as a method on every shape class, then adding an operation means editing every class, and the shape classes slowly accumulate unrelated concerns.

Visitor inverts this. The operation moves into its own object — a visitor — with one method per element type. Each element exposes an accept method that calls back the matching visitor method, passing itself. This two-step dispatch (the element picks the visitor method, the runtime picks the element type) is double dispatch: the executed code depends on both the visitor and the element. Adding a new operation is now just writing a new visitor, with no change to the shapes. The catch is the mirror image — adding a new shape forces a change to every visitor.

classDiagram
  class Shape {
    <<interface>>
    +accept(v Visitor) double
  }
  class Circle {
    +radius: double
    +accept(v Visitor) double
  }
  class Rectangle {
    +width: double
    +height: double
    +accept(v Visitor) double
  }
  class Visitor {
    <<interface>>
    +visitCircle(c Circle) double
    +visitRectangle(r Rectangle) double
  }
  class AreaVisitor {
    +visitCircle(c Circle) double
    +visitRectangle(r Rectangle) double
  }
  Shape <|.. Circle
  Shape <|.. Rectangle
  Visitor <|.. AreaVisitor
  Circle ..> Visitor : accept
  Rectangle ..> Visitor : accept
Each element accepts a visitor and dispatches to the method for its own type
  • Shape (Element) — the interface for the structure’s members. It declares an accept method that takes a visitor.
  • Circle, Rectangle — concrete elements. Each accept calls back the visitor method specific to its type, passing itself.
  • Visitor — the interface declaring one visit method per element type.
  • AreaVisitor — a concrete operation. Each visit method implements the operation for one element type.

A shape hierarchy with an area operation packaged as a visitor. New operations become new visitors; the shapes never change.

interface ShapeVisitor {
visitCircle(c: Circle): number;
visitRectangle(r: Rectangle): number;
}
interface Shape {
accept(v: ShapeVisitor): number;
}
class Circle implements Shape {
constructor(readonly radius: number) {}
accept(v: ShapeVisitor): number {
return v.visitCircle(this);
}
}
class Rectangle implements Shape {
constructor(readonly width: number, readonly height: number) {}
accept(v: ShapeVisitor): number {
return v.visitRectangle(this);
}
}
class AreaVisitor implements ShapeVisitor {
visitCircle(c: Circle): number {
return Math.PI * c.radius * c.radius;
}
visitRectangle(r: Rectangle): number {
return r.width * r.height;
}
}
const shapes: Shape[] = [new Circle(1), new Rectangle(2, 3)];
const area = new AreaVisitor();
for (const s of shapes) {
console.log(s.accept(area).toFixed(2)); // 3.14, then 6.00
}
  • Pro: add new operations over a structure by writing a new visitor, with no change to the element classes.
  • Pro: related behaviour for one operation lives together in one visitor instead of being smeared across every element.
  • Con: adding a new element type forces a change to every visitor — the exact opposite trade-off, so use it only when the set of elements is stable.
  • Con: visitors often need access to element internals, which can weaken encapsulation.
  • Con: the double-dispatch ceremony (accept plus visit methods) is verbose for small hierarchies.
  • Composite trees are the canonical structure a visitor walks; a visitor traverses the composite and applies an operation at each node.
  • Iterator can supply the elements to a visitor, separating how you traverse from what you do at each element.
What does the Visitor pattern let you add without changing the element classes?
What is double dispatch in the Visitor pattern?
What is the chief drawback of Visitor?