The Visitor Design Pattern is a behavioral pattern that lets you add new operations to existing object structures without modifying their classes.
It achieves this by allowing you to separate the algorithm from the objects it operates on.
It’s particularly useful in situations where:
if-else
or instanceof
checks.The Visitor Pattern lets you externalize operations into separate visitor classes. Each visitor implements behavior for every element type, while the elements simply accept the visitor. This keeps your data structures clean and your logic modular and extensible.
Let’s walk through a real-world example to see how we can apply the Visitor Pattern to cleanly separate behavior from structure and make our system easier to extend without touching existing classes.
Imagine you’re building a vector graphics editor that supports multiple shape types:
Circle
Rectangle
Triangle
Each shape is part of a common hierarchy and must support a variety of operations, such as:
The simplest approach is to add all of these methods to each shape class:
This solution seems fine for a couple of operations, but quickly becomes problematic as new operations or shape types are added.
Each shape class now contains multiple unrelated responsibilities:
This bloats the class and makes it harder to maintain.
If you need to add a new operation (e.g., generatePdf()
), you must:
This violates the Open/Closed Principle — the classes should be open for extension but closed for modification.
What if the shape classes are part of a third-party library or generated code? You can't easily add new behavior directly.
We need a solution that lets us:
instanceof
checks or using type switches to handle different shapesThis is exactly what the Visitor Design Pattern is designed to solve.
The Visitor Design Pattern lets you separate algorithms from the objects on which they operate. It enables you to add new operations to a class hierarchy without modifying the classes themselves.
This is especially helpful when you have:
Shape
)void accept(Visitor visitor);
Circle
, Rectangle
)Element
interface.accept()
method, they call back the visitor’s corresponding method using visitor.visitX(this)
.visit()
methods — one for each concrete element type.AreaCalculatorVisitor
)Visitor
interface.Lets refactor our graphics system with multiple shapes (Circle
, Rectangle
) using Visitor Pattern to perform two operations:
Shape
Interface (Element)All shapes must accept a visitor.
Each shape class implements accept()
and delegates to the visitor.
Each method corresponds to a shape type.
Now you can operate on the shape structure using any visitor.
JsonExporterVisitor
) without touching shapesinstanceof
or type-checking