Visitor Pattern¶
Category: Behavioral Pattern
Overview¶
Represent an operation to be performed on elements of an object structure. This pattern lets you define new operations without changing the classes of the elements on which it operates, separating algorithms from the objects they operate on.
Usage Guidelines¶
Use when:
- Need to perform many distinct operations on object structure
- Operations change more frequently than object structure
- Want to keep related operations together
- Object structure is stable but operations vary
Avoid when:
- Object structure changes frequently
- Only a few operations exist
- Operations are simple and well-suited as methods
- Element interfaces change often
Implementation¶
from __future__ import annotations
from abc import ABC, abstractmethod
class ShapeVisitor(ABC):
"""Abstract visitor for shape operations."""
@abstractmethod
def visit_circle(self, circle: Circle) -> str:
"""Visit a circle."""
pass
@abstractmethod
def visit_rectangle(self, rectangle: Rectangle) -> str:
"""Visit a rectangle."""
pass
class Shape(ABC):
"""Abstract shape that accepts visitors."""
@abstractmethod
def accept(self, visitor: ShapeVisitor) -> str:
"""Accept a visitor."""
pass
class Circle(Shape):
"""Concrete circle shape."""
def __init__(self, radius: float) -> None:
"""Initialize circle."""
self.radius = radius
def accept(self, visitor: ShapeVisitor) -> str:
"""Accept visitor for circle."""
return visitor.visit_circle(self)
class Rectangle(Shape):
"""Concrete rectangle shape."""
def __init__(self, width: float, height: float) -> None:
"""Initialize rectangle."""
self.width = width
self.height = height
def accept(self, visitor: ShapeVisitor) -> str:
"""Accept visitor for rectangle."""
return visitor.visit_rectangle(self)
class AreaCalculator(ShapeVisitor):
"""Visitor that calculates areas of shapes."""
def visit_circle(self, circle: Circle) -> str:
"""Calculate circle area."""
import math
area = math.pi * circle.radius ** 2
return f"Circle area: {area:.2f}"
def visit_rectangle(self, rectangle: Rectangle) -> str:
"""Calculate rectangle area."""
area = rectangle.width * rectangle.height
return f"Rectangle area: {area:.2f}"
Usage¶
# Create shapes
circle = Circle(5.0)
rectangle = Rectangle(4.0, 6.0)
# Calculate areas
area_calc = AreaCalculator()
print(circle.accept(area_calc)) # Circle area: 78.54
print(rectangle.accept(area_calc)) # Rectangle area: 24.00
Trade-offs¶
Benefits:
- Add new operations without modifying elements (Open/Closed Principle)
- Groups related operations in visitor classes (Single Responsibility)
- Visitor can accumulate state while traversing
- Separates algorithms from object structure
Drawbacks:
- Visitor may need access to element internals breaking encapsulation
- Adding new element types requires updating all visitors
- Circular dependencies between element and visitor interfaces
- Double dispatch mechanism can be confusing
Real-World Examples¶
- Compiler AST operations like type checking and code generation
- Document processing with rendering, exporting, validating
- File system operations computing sizes, searching, archiving
- Graphics rendering for different shapes
Related Patterns¶
- Composite
- Iterator
- Interpreter
- Strategy
API Reference¶
design_patterns.behavioral.visitor
¶
Visitor Pattern Module
The Visitor pattern represents an operation to be performed on elements of an object structure. It lets you define new operations without changing the classes of the elements on which it operates. This pattern separates algorithms from the objects they operate on.
Example
Calculating areas and exporting shapes:
AreaCalculator
¶
Bases: ShapeVisitor
Visitor that calculates areas of shapes.
Source code in src/design_patterns/behavioral/visitor.py
visit_circle(circle)
¶
Calculate circle area.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circle
|
Circle
|
Circle to calculate. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Area description. |
Source code in src/design_patterns/behavioral/visitor.py
visit_rectangle(rectangle)
¶
Calculate rectangle area.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rectangle
|
Rectangle
|
Rectangle to calculate. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Area description. |
Source code in src/design_patterns/behavioral/visitor.py
Circle
¶
Bases: Shape
Concrete circle shape.
Source code in src/design_patterns/behavioral/visitor.py
__init__(radius)
¶
accept(visitor)
¶
Accept visitor for circle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
visitor
|
ShapeVisitor
|
Visitor to accept. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Result of visit. |
JSONExporter
¶
Bases: ShapeVisitor
Visitor that exports shapes to JSON format.
Source code in src/design_patterns/behavioral/visitor.py
visit_circle(circle)
¶
Export circle to JSON.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circle
|
Circle
|
Circle to export. |
required |
Returns:
| Type | Description |
|---|---|
str
|
JSON representation. |
visit_rectangle(rectangle)
¶
Export rectangle to JSON.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rectangle
|
Rectangle
|
Rectangle to export. |
required |
Returns:
| Type | Description |
|---|---|
str
|
JSON representation. |
Source code in src/design_patterns/behavioral/visitor.py
PerimeterCalculator
¶
Bases: ShapeVisitor
Visitor that calculates perimeters of shapes.
Source code in src/design_patterns/behavioral/visitor.py
visit_circle(circle)
¶
Calculate circle perimeter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circle
|
Circle
|
Circle to calculate. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Perimeter description. |
Source code in src/design_patterns/behavioral/visitor.py
visit_rectangle(rectangle)
¶
Calculate rectangle perimeter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rectangle
|
Rectangle
|
Rectangle to calculate. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Perimeter description. |
Source code in src/design_patterns/behavioral/visitor.py
visit_triangle(triangle)
¶
Calculate triangle perimeter (assumes right triangle).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
triangle
|
Triangle
|
Triangle to calculate. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Perimeter description. |
Source code in src/design_patterns/behavioral/visitor.py
Rectangle
¶
Bases: Shape
Concrete rectangle shape.
Source code in src/design_patterns/behavioral/visitor.py
__init__(width, height)
¶
Initialize rectangle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
width
|
float
|
Rectangle width. |
required |
height
|
float
|
Rectangle height. |
required |
accept(visitor)
¶
Accept visitor for rectangle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
visitor
|
ShapeVisitor
|
Visitor to accept. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Result of visit. |
Shape
¶
Bases: ABC
Abstract shape that accepts visitors.
Source code in src/design_patterns/behavioral/visitor.py
accept(visitor)
abstractmethod
¶
Accept a visitor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
visitor
|
ShapeVisitor
|
Visitor to accept. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Result of the visit. |
ShapeCollection
¶
Collection of shapes that can be visited.
Source code in src/design_patterns/behavioral/visitor.py
__init__()
¶
accept_all(visitor)
¶
Apply visitor to all shapes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
visitor
|
ShapeVisitor
|
Visitor to apply. |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
List of results from visiting each shape. |
Source code in src/design_patterns/behavioral/visitor.py
ShapeVisitor
¶
Bases: ABC
Abstract visitor for shape operations.
Source code in src/design_patterns/behavioral/visitor.py
visit_circle(circle)
abstractmethod
¶
Visit a circle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circle
|
Circle
|
Circle to visit. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Result of visiting the circle. |
visit_rectangle(rectangle)
abstractmethod
¶
Visit a rectangle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rectangle
|
Rectangle
|
Rectangle to visit. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Result of visiting the rectangle. |
Triangle
¶
Bases: Shape
Concrete triangle shape.
Source code in src/design_patterns/behavioral/visitor.py
__init__(base, height)
¶
Initialize triangle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base
|
float
|
Triangle base. |
required |
height
|
float
|
Triangle height. |
required |
accept(visitor)
¶
Accept visitor for triangle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
visitor
|
ShapeVisitor
|
Visitor to accept. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Result of visit. |
XMLExporter
¶
Bases: ShapeVisitor
Visitor that exports shapes to XML format.
Source code in src/design_patterns/behavioral/visitor.py
visit_circle(circle)
¶
Export circle to XML.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circle
|
Circle
|
Circle to export. |
required |
Returns:
| Type | Description |
|---|---|
str
|
XML representation. |
visit_rectangle(rectangle)
¶
Export rectangle to XML.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rectangle
|
Rectangle
|
Rectangle to export. |
required |
Returns:
| Type | Description |
|---|---|
str
|
XML representation. |