Behavioral PatternADVANCED8 min deep dive

Visitor Pattern Pattern

Lets you separate algorithms from the objects on which they operate by using Double Dispatch.

Real-World Mental Model / Metaphor:

A tax auditor visiting different properties (Residential home, Commercial factory, Agricultural farm). Each property accepts the auditor (`property.accept(auditor)`), and the auditor executes property-specific tax calculations without changing the property's class.

Interactive Runtime Simulator

Test and tweak inputs dynamically

Interactive Visitor Pattern SimulatorLIVE PLAYGROUND

Interact with the controls below to see how this design pattern executes in memory at runtime.

Category: Behavioral
Configure Chain Handlers (Toggle Failures):
CHAIN PIPELINE STATUS
Step 1: AuthHandler.check() -> PASSED
Step 2: RateLimitHandler.check() -> PASSED
Step 3: PayloadGuard.check() -> PASSED
The Problem It Solves

Polluting domain model classes (e.g. `Circle`, `Rectangle`, `Dot`) with unrelated exporting, linting, or auditing code.

The Architectural Solution

Define a Visitor interface with an `export(Circle)`, `export(Rectangle)` method for each element type. Elements implement `accept(Visitor)` and dispatch `visitor.export(this)` (Double Dispatch).

Pattern Structure & Participants

ParticipantRole & ResponsibilityType
Visitor (Interface)Declares visit(ConcreteElementA), visit(ConcreteElementB)interface
Element (Interface)Declares accept(Visitor)interface
XmlExportVisitor, JsonExportVisitorConcrete operations over all elementsclass
Collaboration Rules:
  • element.accept(visitor) -> executes visitor.visit(element)
  • New operations are added by writing a new Visitor class without touching Elements

Production Code Implementation

Visitor Pattern.java
public interface Shape {
    void accept(Visitor visitor);
}

public interface Visitor {
    void visitCircle(Circle circle);
    void visitRectangle(Rectangle rect);
}

public class Circle implements Shape {
    public int radius = 10;
    public void accept(Visitor visitor) { visitor.visitCircle(this); }
}

public class Rectangle implements Shape {
    public int width = 20, height = 30;
    public void accept(Visitor visitor) { visitor.visitRectangle(this); }
}

// Adding new export operation without modifying Circle or Rectangle!
public class SvgExportVisitor implements Visitor {
    public void visitCircle(Circle c) {
        System.out.println("<circle r='" + c.radius + "' />");
    }
    public void visitRectangle(Rectangle r) {
        System.out.println("<rect width='" + r.width + "' height='" + r.height + "' />");
    }
}
When to Use:
  • •When you need to perform an operation on all elements of a complex object structure (such as an AST tree).
  • •When an object structure rarely changes, but you frequently need to define new operations over it.
When to Avoid (Anti-Pattern):
  • •When the element hierarchy changes frequently (adding a new Element requires updating every single Visitor interface and class).

Staff Engineering Interview Insights & Gotchas

  • Explain 'Double Dispatch': the method that gets executed depends on both the type of the receiver AND the type of the argument.