What It Is
The Visitor pattern adds a new operation to a family of classes without changing them. The classes learn one thing once — accept($visitor) — and from then on, every new operation is just a new visitor with one method per class.
It flips the usual trade: adding operations becomes easy, adding classes becomes the expensive change.
Example: Operations over Shapes
The shapes never change again. Printing areas is one visitor, naming them is another — and an export-to-SVG visitor tomorrow would touch neither Circle nor Rectangle.
New operations, untouched classes
Both visitors walk the same shape list through the same accept door. Each visitor holds the whole operation in one place, instead of scattering it across the classes.
Output: PHP script
<?php
interface Shape {
public function accept(Visitor $v): void;
}
class Circle implements Shape {
public function __construct(public float $radius) {}
public function accept(Visitor $v): void {
$v->visitCircle($this);
}
}
class Rectangle implements Shape {
public function __construct(
public float $width,
public float $height,
) {}
public function accept(Visitor $v): void {
$v->visitRectangle($this);
}
}
// A visitor is one whole operation over every shape class.
interface Visitor {
public function visitCircle(Circle $c): void;
public function visitRectangle(Rectangle $r): void;
}
class AreaPrinter implements Visitor {
public function visitCircle(Circle $c): void {
printf("circle area: %.2f\n", 3.14159 * $c->radius * $c->radius);
}
public function visitRectangle(Rectangle $r): void {
printf("rectangle area: %.2f\n", $r->width * $r->height);
}
}
class NamePrinter implements Visitor {
public function visitCircle(Circle $c): void {
echo "a circle\n";
}
public function visitRectangle(Rectangle $r): void {
echo "a rectangle\n";
}
}
$shapes = [new Circle(2), new Rectangle(3, 4)];
foreach ($shapes as $s) {
$s->accept(new AreaPrinter());
}
foreach ($shapes as $s) {
$s->accept(new NamePrinter());
}
When to Use It
Use it when a stable family of classes keeps growing new operations — compilers visiting syntax trees, documents gaining exporters, shapes gaining renderers and measurers.
Skip it when the class family still grows: every new class forces a change in every visitor. Often an instanceof check or a match on $shape::class does the same job with less machinery — reach for the visitor when the operations, not the classes, are multiplying.
Pros and Cons
Pros: whole operations live in one class each; new operations need zero changes to the visited classes; related logic stops being scattered.
Cons: the double dispatch (accept calling back) confuses newcomers; adding one visited class means editing every visitor; the visited family's interface is frozen early.