# VisitorA intermediate-level guide to Visitor: before-and-after java code and diagrams for a CS student.

## The Burden of Growing Responsibilities

Imagine you are building a geometry library. Initially, your `Circle` and `Rectangle` classes only need to store dimensions like radius or width. But then, requirements grow: you need to print their details, calculate their areas, and eventually export them to XML for web services.

If you follow the simplest path, those mathematical and serialization responsibilities leak into your data classes. Soon, a simple `Circle` class is no longer just a representation of a shape; it is a calculator, a printer, and an XML generator. This creates "bloated" classes that violate the Single Responsibility Principle (SRP), as they are constantly changing every time a new output format or calculation is required.

```java
import java.util.List;

interface Shape {}

class Circle implements Shape {
    private final double radius;
    public Circle(double radius) { this.radius = radius; }
    public double getRadius() { return radius; }
}

class Rectangle implements Shape {
    private final double width;
    private final double height;
    public Rectangle(double w, double h) { this.width = w; this.height = h; }
    public double getWidth() { return width; }
    public double getHeight() { return height; }
}

class ShapePrinter {
    public void printDetails(List<Shape> shapes) {
        for (Shape shape : shapes) {
            if (shape instanceof Circle c) {
                System.out.println("Circle with radius: " + c.getRadius());
            } else if (shape instanceof Rectangle r) {
                System.out.println("Rectangle: " + r.getWidth() + "x" + r.getHeight());
            }
        }
    }

    public void calculateArea(List<Shape> shapes) {
        for (Shape shape : shapes) {
            if (shape instanceof Circle c) {
                System.out.println("Area: " + (Math.PI * c.getRadius() * c.getRadius()));
            } else if (shape instanceof Rectangle r) {
                System.out.println("Area: " + (r.getWidth() * r.getHeight()));
            }
        }
    }
}
```

The current implementation relies on `instanceof` checks inside a single utility class (`ShapePrinter`). Every time you add a new shape, like a `Triangle`, you must find every method that performs type-checking and manually add a new `else if` branch to handle it. This is fragile and difficult to maintain.

```mermaid
classDiagram
    class Shape {
        <<interface>>
    }
    class Circle {
        -radius: double
        +getRadius(): double
    }
    class Rectangle {
        -width: double
        -height: double
        +getWidth(): double
        +getHeight(): double
    }
    class ShapePrinter {
        +printDetails(List~Shape~)
        +calculateArea(List~Shape~)
    }

    Shape <|.. Circle
    Shape <|-- Rectangle
    ShapePrinter ..> Shape : uses
```

## The Solution: The Visitor Pattern

The Visitor pattern solves this by separating the object structure from the operations performed on them. Instead of the shape knowing how to print itself, we introduce a "Visitor" object that contains the logic. 

To make this work, we use a mechanism called **Double Dispatch**. This is a two-step process where the operation's behavior depends on both the type of the visitor and the type of the element it is visiting.

1.  **First Dispatch**: You call `shape.accept(visitor)`. Because `accept` is polymorphic, Java looks at the actual runtime type (e.g., `Circle`) to decide which implementation to run.
2.  **Second Dispatch**: Inside `Circle.accept`, the code calls `visitor.visit(this)`. Since `this` is known to be a `Circle` by the compiler at this point, the correct overloaded `visit` method in the Visitor interface is triggered.

```mermaid
sequenceDiagram
    participant S as Circle
    participant V as PrintVisitor
    S ->> V: accept(visitor)
    V ->> S: visit(this)
    Note right of S: The circle "accepts" the visitor
    Note left of V: The visitor "visits" the specific type
```

```java
import java.util.List;

interface Shape {
    void accept(ShapeVisitor visitor);
}

interface ShapeVisitor {
    void visit(Circle circle);
    void visit(Rectangle rectangle);
}

class Circle implements Shape {
    private final double radius;
    public Circle(double radius) { this.radius = radius; }
    public double getRadius() { return radius; }
    @Override
    public void accept(ShapeVisitor visitor) { visitor.visit(this); }
}

class Rectangle implements Shape {
    private final double width;
    private final double height;
    public Rectangle(double w, double h) { this.width = w; this.height = h; }
    public double getWidth() { return width; }
    public double getHeight() { return height; }
    @Override
    public void accept(ShapeVisitor visitor) { visitor.visit(this); }
}

class PrintVisitor implements ShapeVisitor {
    @Override
    public void visit(Circle c) {
        System.out.println("Circle with radius: " + c.getRadius());
    }
    @Override
    public void visit(Rectangle r) {
        System.out.println("Rectangle: " + r.getWidth() + "x" + r.getHeight());
    }
}

class AreaVisitor implements ShapeVisitor {
    @Override
    public void visit(Circle c) {
        System.out.println("Area: " + (Math.PI * c.getRadius() * c.getRadius()));
    }
    @Override
    public void visit(Rectangle r) {
        System.out.println("Area: " + (r.getWidth() * r.getHeight()));
    }
}
```

Now, `Circle` and `Rectangle` are "thin." They only know how to accept a visitor. All the complex logic for printing or calculating area is encapsulated in specialized classes like `PrintVisitor` and `AreaVisitor`.

```mermaid
classDiagram
    class Shape {
        <<interface>>
        +accept(ShapeVisitor)
    }
    class ShapeVisitor {
        <<interface>>
        +visit(Circle)
        +visit(Rectangle)
    }
    class Circle {
        -radius: double
        +getRadius(): double
        +accept(ShapeVisitor)
    }
    class Rectangle {
        -width: double
        -height: double
        +getWidth(): double
        +getHeight(): double
        +accept(ShapeVisitor)
    }
    class PrintVisitor {
        +visit(Circle)
        +visit(Rectangle)
    }
    class AreaVisitor {
        +visit(Circle)
        +visit(Rectangle)
    }

    Shape <|.. Circle
    Shape <|-- Rectangle
    ShapeVisitor <|.. PrintVisitor
    ShapeVisitor <|.. AreaVisitor
    Shape ..> ShapeVisitor : accepts
    PrintVisitor ..> Circle : visits
    AreaVisitor ..> Rectangle : visits
```

## When to Use It, and When Not To

The Visitor pattern is a powerful tool for achieving the Open-Closed Principle (OCP), but it comes with a significant architectural trade-off.

**Use Visitor when:**
*   Your object hierarchy is stable (you rarely add new types of shapes).
*   You frequently need to add new operations (like `exportToJSON`, `calculatePerimeter`, etc.) without modifying the shape classes themselves.
*   You want to keep your data objects "pure" and focused only on their properties.

**Avoid Visitor when:**
*   Your class hierarchy is unstable. Adding a new element type (e.g., adding `Triangle` to `Shape`) requires you to update the `ShapeVisitor` interface and *every single* concrete visitor implementation in your entire codebase. This makes adding new types an expensive "Shotgun Surgery" operation.

## Takeaways

- Use Visitor to keep data classes thin and focused on their core properties.
- The pattern is best when your object hierarchy is stable but you need to add many different operations over time.
- Beware: Adding a new element type requires updating the entire Visitor interface.

## Usage

**Old usage with a bloated utility class performing type checks.**

```java
List<Shape> shapes = List.of(new Circle(5), new Rectangle(2, 3));
shapePrinter.printDetails(shapes);
shapePrinter.calculateArea(shapes);
```

**New usage where operations are decoupled from the hierarchy.**

```java
List<Shape> shapes = List.of(new Circle(5), new Rectangle(2, 3));
shapes.forEach(s -> s.accept(new PrintVisitor()));
shapes.forEach(s -> s.accept(new AreaVisitor()));
```
