The Visitor pattern allows you to define new operations on a set of related objects without modifying their classes. Instead of changing each class to add functionality, you create a separate visitor object that “visits” each one to perform the desired action.
In object-oriented design, you might have a class hierarchy where all the classes share a common interface. Over time, you may want to add operations to these classes. Modifying each one can get messy—especially if you can’t change the original code (e.g., it comes from a third-party library). The Visitor pattern solves this by moving those new operations into a visitor class, keeping your data classes clean and unchanged.
Use the Visitor pattern when:
Avoid this pattern if:
The pattern involves two key roles:
visit
methods for each object type.accept()
method, which passes control to the visitor.Each element delegates the operation to the visitor, which knows how to handle it based on its type.
Imagine you’re on a zoo tour. Each animal (element) allows a tour guide (visitor) to interact with it. The guide knows how to handle each animal—feeding the giraffes, photographing the lions, etc. The animals don’t need to know what the guide is doing—they just accept the guide.
Here’s a basic example in Python:
from abc import ABC, abstractmethod
# Visitor interface
class Visitor(ABC):
@abstractmethod
def visit_element_a(self, element):
pass
@abstractmethod
def visit_element_b(self, element):
pass
# Concrete Visitor
class ConcreteVisitor(Visitor):
def visit_element_a(self, element):
print("Visiting Element A")
def visit_element_b(self, element):
print("Visiting Element B")
# Element base class
class Element(ABC):
@abstractmethod
def accept(self, visitor: Visitor):
pass
# Concrete Elements
class ElementA(Element):
def accept(self, visitor: Visitor):
visitor.visit_element_a(self)
class ElementB(Element):
def accept(self, visitor: Visitor):
visitor.visit_element_b(self)
visitor = ConcreteVisitor()
a = ElementA()
b = ElementB()
a.accept(visitor) # Visiting Element A
b.accept(visitor) # Visiting Element B
Each element accepts the visitor, which then performs a type-specific operation.
See the complete implementation on GitHub: Visitor Pattern on GitHub