Command Palette

Search for a command to run...

PHASE 2Beginner ~7 min· topic 22 of 22Behavioral

Behavioral 10 — Visitor

In one line

Add new operations to a group of classes without changing those classes. The operation 'visits' each object and does the right thing for its type.

0/22 · 0%

Think of it like this

A tax inspector visiting different shops. The shops stay the same; the inspector knows how to calculate tax for a grocery store vs a jewellery store. A new inspector (say, a fire-safety checker) can visit the same shops.

Key ideas

  1. 01

    Use it when the classes are stable but the operations keep growing: e.g. shapes {Circle, Square} with operations area, perimeter, drawToSvg, exportToJson...

  2. 02

    Double dispatch: element.accept(visitor) calls visitor.visit(this), so Java picks the right method by BOTH the element type and the visitor type.

  3. 03

    Trade-off in one line: new operation = easy (one new visitor class); new element type = hard (every visitor must change).

  4. 04

    Modern Java alternative: sealed interfaces + pattern-matching switch give most of Visitor's benefits with much less code.

  5. 05

    Used in: compilers (walking a syntax tree), file-system scanners, and reporting over a Composite tree.

Java / Spring map

  • →

    java.nio.file.FileVisitor (Files.walkFileTree) is a built-in visitor.

Code & diagrams

Visitor.javajava

Classic visitor, then the modern sealed-interface version of the same idea.

// Classic visitor
public interface Shape { <R> R accept(ShapeVisitor<R> v); }
public record Circle(double r) implements Shape { public <R> R accept(ShapeVisitor<R> v) { return v.visit(this); } }
public record Square(double side) implements Shape { public <R> R accept(ShapeVisitor<R> v) { return v.visit(this); } }

public interface ShapeVisitor<R> { R visit(Circle c); R visit(Square s); }

public final class AreaVisitor implements ShapeVisitor<Double> {
  public Double visit(Circle c) { return Math.PI * c.r() * c.r(); }
  public Double visit(Square s) { return s.side() * s.side(); }
}
// New operation (perimeter, SVG export) = one new visitor. Shapes never change.

// Modern Java 21: sealed + switch does the same with less ceremony
public sealed interface Shape2 permits Circle2, Square2 {}
public record Circle2(double r) implements Shape2 {}
public record Square2(double side) implements Shape2 {}

static double area(Shape2 s) {
  return switch (s) {                      // compiler checks every type is handled
    case Circle2 c -> Math.PI * c.r() * c.r();
    case Square2 q -> q.side() * q.side();
  };
}

Explain without notes

01

Why is adding a new operation easy with Visitor but adding a new element type hard?

Practice

01

Write a SizeVisitor and a SearchVisitor over the file/folder Composite tree.

Trade-offs

  • ↔

    Visitor makes operations easy to add and types hard to add. Choose it only when your type list is stable.

Completion checklist

  • I can explain double dispatch and when sealed-type pattern matching replaces Visitor.

Back to phase