The Visitor Design Pattern is a behavioral design pattern that allows new operations to be added to existing classes without modifying their structure. It separates algorithms from the objects they operate on, improving flexibility and maintainability.
- Adds new functionality without changing existing classes by defining operations inside separate visitor classes.
- Improves modularity and maintainability while making it easier to perform operations on complex object hierarchies.
Example: In an online shopping cart, different items such as books, electronics, and clothing can accept visitors to perform operations like price calculation and discount application. New features can be added without modifying the item classes.

In the Diagram
- Shopping items act as element objects that accept visitors.
- Visitor classes perform operations such as billing or discount calculation.
- New operations can be added easily by creating new visitor classes without changing existing item classes.
Components
The Visitor design pattern consists of several key components that work together to enable its functionality. Here’s a breakdown of these components
- Visitor Interface: This interface declares a visit method for each type of element in the object structure. Each method is designed to handle a specific element type.
- Concrete Visitor: This class implements the Visitor interface and provides the specific behavior for each visit method. It contains the logic for the operations that need to be performed on the elements.
- Element Interface: This interface defines an
acceptmethod that takes a visitor as an argument. This method allows the visitor to visit the concrete elements. - Concrete Elements: These classes implement the Element interface and represent the various types of objects in the structure. Each concrete element defines how it accepts a visitor by calling the corresponding method on the visitor.
- Object Structure: This is the collection of elements (the concrete elements) that the visitor will operate on. It often includes methods to add, remove, and retrieve elements.
Real-World Example
In an online shopping cart, different items like books, electronics, and clothing can accept visitors to perform operations such as price calculation or discount application. This allows new operations to be added without modifying the item classes.
- Shopping items act as element objects.
- Visitors perform operations like billing, discount calculation, or tax computation.
- New features can be added easily by creating new visitor classes.
Uses
The Visitor pattern is commonly used when multiple operations are required on a fixed set of classes.
- Compiler design (syntax tree traversal).
- Calculating taxes, discounts, or reports on objects.
- File system operations (size calculation, backup).
- Processing elements in complex object structures.
Working
The pattern works by separating operations from the object structure using a visitor object.
- Each element in the object structure implements an accept() method.
- The accept() method passes the element to the visitor.
- The visitor defines different operations for different element types.
- New operations can be added by creating new visitor classes.

- The Client creates Visitor objects and passes them to the Object Structure.
- The Object Structure holds a collection of Element objects.
- Each ConcreteElement (A, B) implements the accept(visitor) method.
- The accept() method calls the corresponding visit() method of the Visitor.
- ConcreteVisitor1 and ConcreteVisitor2 define different operations on the same elements.
Implementation Example
Problem statement
Assume a situation whereby you have a set of shapes like circles, squares, and triangles. You want to find the area of each given figure. One option is to add a method that calculates the area of each shape class. Yet, it breaks the open-closed principle, as modifying existing classes is mandatory whenever a new operation emerges.
There are the following steps for implementing Visitor Design Method:
Step 1: Define the Visitor interface
class ShapeVisitor {
public:
void visit(Circle circle);
void visit(Square square);
void visit(Triangle triangle);
};
public interface ShapeVisitor {
void visit(Circle circle);
void visit(Square square);
void visit(Triangle triangle);
}
from abc import ABC, abstractmethod
class ShapeVisitor(ABC):
@abstractmethod
def visit(self, circle):
pass
@abstractmethod
def visit(self, square):
pass
@abstractmethod
def visit(self, triangle):
pass
class ShapeVisitor {
visit(circle) {}
visit(square) {}
visit(triangle) {}
}
Step 2: Define the Element interface
#include <iostream>
#include <vector>
class ShapeVisitor;
class Shape {
public:
virtual void accept(ShapeVisitor& visitor) = 0;
};
public interface Shape {
void accept(ShapeVisitor visitor);
}
from abc import ABC, abstractmethod
class ShapeVisitor:
@abstractmethod
def visit(self, shape):
pass
class Shape(ABC):
@abstractmethod
def accept(self, visitor: ShapeVisitor):
pass
// Define the Shape interface
class Shape {
accept(visitor) {
throw new Error("Method 'accept()' must be implemented.");
}
}
Step 3: Implement Concrete Elements
#include <vector>
class Shape {
public:
virtual void accept(ShapeVisitor* visitor) = 0;
};
class Circle : public Shape {
// Circle specific properties and methods
void accept(ShapeVisitor* visitor) override {
visitor->visit(this);
}
};
class Square : public Shape {
void accept(ShapeVisitor* visitor) override {
visitor->visit(this);
}
};
class Triangle : public Shape {
void accept(ShapeVisitor* visitor) override {
visitor->visit(this);
}
};
public class Circle implements Shape {
// Circle specific properties and methods
@Override
public void accept(ShapeVisitor visitor) {
visitor.visit(this);
}
}
class Square implements Shape {
@Override
public void accept(ShapeVisitor visitor) {
visitor.visit(this);
}
}
class Triangle implements Shape {
@Override
public void accept(ShapeVisitor visitor) {
visitor.visit(this);
}
}
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def accept(self, visitor):
pass
class Circle(Shape):
# Circle specific properties and methods
def accept(self, visitor):
visitor.visit(self)
class Square(Shape):
def accept(self, visitor):
visitor.visit(self)
class Triangle(Shape):
def accept(self, visitor):
visitor.visit(self)
class Shape {
accept(visitor) {
throw new Error('Method not implemented.');
}
}
class Circle extends Shape {
// Circle specific properties and methods
accept(visitor) {
visitor.visit(this);
}
}
class Square extends Shape {
accept(visitor) {
visitor.visit(this);
}
}
class Triangle extends Shape {
accept(visitor) {
visitor.visit(this);
}
}
Step 4: Implement Concrete Visitors
#include <cmath>
#include <iostream>
using namespace std;
class ShapeVisitor {
public:
virtual void visit(double radiusOfCircle) = 0;
virtual void visit(double sideOfSquare) = 0;
virtual void visit(double baseOfTriangle, double heightOfTriangle) = 0;
};
class AreaCalculator : public ShapeVisitor {
double totalArea = 0;
double radiusOfCircle = 5;
double sideOfSquare = 4;
double baseOfTriangle = 3;
double heightOfTriangle = 6;
public:
void visit(double radiusOfCircle) override {
// Calculate area of circle and update totalArea
totalArea += M_PI * pow(radiusOfCircle, 2);
}
void visit(double sideOfSquare) override {
// Calculate area of square and update totalArea
totalArea += pow(sideOfSquare, 2);
}
void visit(double baseOfTriangle, double heightOfTriangle) override {
// Calculate area of triangle and update totalArea
totalArea += (baseOfTriangle * heightOfTriangle) / 2;
}
double getTotalArea() {
return totalArea;
}
};
public class AreaCalculator implements ShapeVisitor {
double totalArea = 0;
double radiusOfCircle = 5;
double sideOfSquare = 4;
double baseOfTriangle = 3;
double heightOfTriangle = 6;
@Override
public void visit(Circle circle) {
// Calculate area of circle and update totalArea
totalArea += Math.PI * Math.pow(radiusOfCircle, 2);
}
@Override
public void visit(Square square) {
// Calculate area of square and update totalArea
totalArea += Math.pow(sideOfSquare, 2);
}
@Override
public void visit(Triangle triangle) {
// Calculate area of triangle and update totalArea
totalArea += (baseOfTriangle * heightOfTriangle) / 2;
}
public double getTotalArea() {
return totalArea;
}
}
from abc import ABC, abstractmethod
import math
class ShapeVisitor(ABC):
@abstractmethod
def visit(self, shape):
pass
class AreaCalculator(ShapeVisitor):
def __init__(self):
self.totalArea = 0
def visit(self, shape):
if isinstance(shape, Circle):
# Calculate area of circle and update totalArea
self.totalArea += math.pi * shape.radiusOfCircle ** 2
elif isinstance(shape, Square):
# Calculate area of square and update totalArea
self.totalArea += shape.sideOfSquare ** 2
elif isinstance(shape, Triangle):
# Calculate area of triangle and update totalArea
self.totalArea += (shape.baseOfTriangle * shape.heightOfTriangle) / 2
def get_total_area(self):
return self.totalArea
# Example classes for Circle, Square, and Triangle
class Circle:
def __init__(self, radiusOfCircle):
self.radiusOfCircle = radiusOfCircle
class Square:
def __init__(self, sideOfSquare):
self.sideOfSquare = sideOfSquare
class Triangle:
def __init__(self, baseOfTriangle, heightOfTriangle):
self.baseOfTriangle = baseOfTriangle
self.heightOfTriangle = heightOfTriangle
class AreaCalculator {
constructor() {
this.totalArea = 0;
}
visit(shape) {
if (shape instanceof Circle) {
// Calculate area of circle and update totalArea
this.totalArea += Math.PI * Math.pow(shape.radiusOfCircle, 2);
} else if (shape instanceof Square) {
// Calculate area of square and update totalArea
this.totalArea += Math.pow(shape.sideOfSquare, 2);
} else if (shape instanceof Triangle) {
// Calculate area of triangle and update totalArea
this.totalArea += (shape.baseOfTriangle * shape.heightOfTriangle) / 2;
}
}
getTotalArea() {
return this.totalArea;
}
}
// Example classes for Circle, Square, and Triangle
class Circle {
constructor(radiusOfCircle) {
this.radiusOfCircle = radiusOfCircle;
}
}
class Square {
constructor(sideOfSquare) {
this.sideOfSquare = sideOfSquare;
}
}
class Triangle {
constructor(baseOfTriangle, heightOfTriangle) {
this.baseOfTriangle = baseOfTriangle;
this.heightOfTriangle = heightOfTriangle;
}
}
Complete Code of Visitor Design Pattern
The overall code of the above example is
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
// Forward Declarations
class Circle;
class Square;
class Triangle;
// Visitor interface
class ShapeVisitor {
public:
virtual void visit(Circle* circle) = 0;
virtual void visit(Square* square) = 0;
virtual void visit(Triangle* triangle) = 0;
};
// Element interface
class Shape {
public:
virtual void accept(ShapeVisitor* visitor) = 0;
virtual ~Shape() {}
};
// Concrete Elements
class Circle : public Shape {
public:
void accept(ShapeVisitor* visitor) override {
visitor->visit(this);
}
};
class Square : public Shape {
public:
void accept(ShapeVisitor* visitor) override {
visitor->visit(this);
}
};
class Triangle : public Shape {
public:
void accept(ShapeVisitor* visitor) override {
visitor->visit(this);
}
};
// Concrete Visitor
class AreaCalculator : public ShapeVisitor {
private:
double totalArea = 0;
public:
void visit(Circle* circle) override {
double radius = 5;
totalArea += M_PI * radius * radius;
}
void visit(Square* square) override {
double side = 4;
totalArea += side * side;
}
void visit(Triangle* triangle) override {
double base = 3;
double height = 6;
totalArea += (base * height) / 2;
}
double getTotalArea() {
return totalArea;
}
};
// Main function
int main() {
vector<Shape*> shapes;
shapes.push_back(new Circle());
shapes.push_back(new Square());
shapes.push_back(new Triangle());
AreaCalculator areaCalculator;
for (Shape* shape : shapes) {
shape->accept(&areaCalculator);
}
cout << "Total area: "
<< areaCalculator.getTotalArea()
<< endl;
for (Shape* shape : shapes) {
delete shape;
}
return 0;
}
import java.util.ArrayList;
import java.util.List;
// Visitor interface
interface ShapeVisitor {
void visit(Circle circle);
void visit(Square square);
void visit(Triangle triangle);
}
// Element interface
interface Shape {
void accept(ShapeVisitor visitor);
}
// Concrete Elements
class Circle implements Shape {
@Override
public void accept(ShapeVisitor visitor) {
visitor.visit(this);
}
}
class Square implements Shape {
@Override
public void accept(ShapeVisitor visitor) {
visitor.visit(this);
}
}
class Triangle implements Shape {
@Override
public void accept(ShapeVisitor visitor) {
visitor.visit(this);
}
}
// Concrete Visitors
class AreaCalculator implements ShapeVisitor {
private double totalArea = 0;
double radiusOfCircle = 5;
double sideOfSquare = 4;
double baseOfTriangle = 3;
double heightOfTriangle = 6;
@Override
public void visit(Circle circle) {
// Calculate area of circle and update totalArea
totalArea += Math.PI * Math.pow(radiusOfCircle, 2);
}
@Override
public void visit(Square square) {
// Calculate area of square and update totalArea
totalArea += Math.pow(sideOfSquare, 2);
}
@Override
public void visit(Triangle triangle) {
// Calculate area of triangle and update totalArea
totalArea += (baseOfTriangle * heightOfTriangle) / 2;
}
public double getTotalArea() {
return totalArea;
}
}
// Main class
public class Main {
public static void main(String[] args) {
List<Shape> shapes = new ArrayList<>();
shapes.add(new Circle());
shapes.add(new Square());
shapes.add(new Triangle());
AreaCalculator areaCalculator = new AreaCalculator();
for (Shape shape : shapes) {
shape.accept(areaCalculator);
}
System.out.println("Total area: " + areaCalculator.getTotalArea());
}
}
from abc import ABC, abstractmethod
import math
# Visitor interface
class ShapeVisitor(ABC):
@abstractmethod
def visit(self, shape):
pass
# Element interface
class Shape(ABC):
@abstractmethod
def accept(self, visitor):
pass
# Concrete Elements
class Circle(Shape):
def accept(self, visitor):
visitor.visit(self)
class Square(Shape):
def accept(self, visitor):
visitor.visit(self)
class Triangle(Shape):
def accept(self, visitor):
visitor.visit(self)
# Concrete Visitors
class AreaCalculator(ShapeVisitor):
def __init__(self):
self.totalArea = 0
self.radiusOfCircle = 5
self.sideOfSquare = 4
self.baseOfTriangle = 3
self.heightOfTriangle = 6
def visit(self, shape):
if isinstance(shape, Circle):
self.totalArea += math.pi * self.radiusOfCircle ** 2
elif isinstance(shape, Square):
self.totalArea += self.sideOfSquare ** 2
elif isinstance(shape, Triangle):
self.totalArea += (self.baseOfTriangle * self.heightOfTriangle) / 2
def getTotalArea(self):
return self.totalArea
# Main block
if __name__ == '__main__':
shapes = [Circle(), Square(), Triangle()]
areaCalculator = AreaCalculator()
for shape in shapes:
shape.accept(areaCalculator)
print('Total area:', areaCalculator.getTotalArea())
// Visitor interface
class ShapeVisitor {
visit(shape) {}
}
// Element interface
class Shape {
accept(visitor) {}
}
// Concrete Elements
class Circle extends Shape {
accept(visitor) {
visitor.visit(this);
}
}
class Square extends Shape {
accept(visitor) {
visitor.visit(this);
}
}
class Triangle extends Shape {
accept(visitor) {
visitor.visit(this);
}
}
// Concrete Visitors
class AreaCalculator extends ShapeVisitor {
constructor() {
super();
this.totalArea = 0;
this.radiusOfCircle = 5;
this.sideOfSquare = 4;
this.baseOfTriangle = 3;
this.heightOfTriangle = 6;
}
visit(shape) {
if (shape instanceof Circle) {
this.totalArea += Math.PI * Math.pow(this.radiusOfCircle, 2);
} else if (shape instanceof Square) {
this.totalArea += Math.pow(this.sideOfSquare, 2);
} else if (shape instanceof Triangle) {
this.totalArea += (this.baseOfTriangle * this.heightOfTriangle) / 2;
}
}
getTotalArea() {
return this.totalArea;
}
}
// Main block
const shapes = [new Circle(), new Square(), new Triangle()];
const areaCalculator = new AreaCalculator();
for (const shape of shapes) {
shape.accept(areaCalculator);
}
console.log(`Total area: ${areaCalculator.getTotalArea()}`);
Output
Total area: 103.53981633974483
Advantages
The Pros of Visitor Design Pattern are
- Separation of Concerns: This pattern keeps operations separate from the objects themselves, making it easier to manage and understand the code.
- Easy to Add New Features: You can introduce new operations simply by creating new visitor classes without changing the existing objects. This makes the system flexible.
- Centralized Logic: All the operations are in one place (the visitor), which helps you see how different tasks interact with your objects.
- Easier Maintenance: If you need to update or fix something, you can do it in the visitor class without touching the object classes, making maintenance simpler.
- Type Safety: Each visitor method is specific to an object type, which helps catch errors early and ensures the right operations are applied.
Disadvantages
The Cons of Visitor Design Pattern are
- Added Complexity: It can make your code more complicated, especially if you have many types of objects or operations to manage.
- Challenging to Add New Objects: While adding new operations is easy, introducing new types of objects requires changes to all visitor classes, which can be a hassle.
- Tight Coupling: Visitors need to know about all the specific object types, which can create a dependency and make your design less flexible.
- More Classes to Manage: This pattern can lead to a lot of extra classes and interfaces, which can clutter your codebase and make it harder to navigate.
- Not Ideal for Frequent Changes: If your object types change often, the Visitor pattern can become a burden, as you'd need to update multiple visitor classes each time.