Behavioral · DWG NO. 024

Visitor

Add new operations over a set of classes without modifying those classes, by moving the operation into a separate visitor.

TypeBehavioral
ScopeObject structure
ComplexityMedium–High
Common inAST tooling (linters/compilers), document export, shape geometry

A new operation means editing every class in the hierarchy

A set of shape classes (Circle, Square, Triangle) that need `calculateArea()`, then later `exportToSVG()`, then later `toJSON()` — each new operation means opening every shape class again and adding a method, even though the shapes themselves haven't changed.

The operation visits the structure, not the other way around

Each element implements one `accept(visitor)` method that calls back into the visitor with itself (`visitor.visitCircle(this)`). New operations are new visitor classes — the element classes never change again after `accept` is added once.

CircleSquareaccept(v)accept(v)AreaVisitor(or ExportVisitor)

Shapes visited by an interchangeable operation

Adding `ExportVisitor` later needs zero changes to `Circle` or `Square` — only a new visitor class.

shape-visitor.js
class Circle {
  constructor(radius) { this.radius = radius; }
  accept(visitor) { return visitor.visitCircle(this); }
}
class Square {
  constructor(side) { this.side = side; }
  accept(visitor) { return visitor.visitSquare(this); }
}

const AreaVisitor = {
  visitCircle: (c) => Math.PI * c.radius ** 2,
  visitSquare: (s) => s.side ** 2,
};

const ExportVisitor = {
  visitCircle: (c) => `<circle r="${c.radius}"/>`,
  visitSquare: (s) => `<rect width="${s.side}" height="${s.side}"/>`,
};

const shapes = [new Circle(3), new Square(4)];

console.log(shapes.map((s) => s.accept(AreaVisitor)));
console.log(shapes.map((s) => s.accept(ExportVisitor)));

Trade-offs