Visitor design pattern (original) (raw)
Last Updated : 22 May, 2026
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.

UML Diagram
- 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
C++ `
class ShapeVisitor { public: void visit(Circle circle); void visit(Square square); void visit(Triangle triangle); };
Java
public interface ShapeVisitor { void visit(Circle circle); void visit(Square square); void visit(Triangle triangle); }
Python
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):
passJavaScript
class ShapeVisitor { visit(circle) {} visit(square) {} visit(triangle) {} }
`
Step 2: Define the Element interface
C++ `
#include #include
class ShapeVisitor;
class Shape { public: virtual void accept(ShapeVisitor& visitor) = 0; };
Java
public interface Shape { void accept(ShapeVisitor visitor); }
Python
from abc import ABC, abstractmethod
class ShapeVisitor: @abstractmethod def visit(self, shape): pass
class Shape(ABC): @abstractmethod def accept(self, visitor: ShapeVisitor): pass
JavaScript
// Define the Shape interface class Shape { accept(visitor) { throw new Error("Method 'accept()' must be implemented."); } }
`
Step 3: Implement Concrete Elements
C++ `
#include
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); } };
Java
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); } }
Python
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)
JavaScript
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
C++ `
#include #include 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;
}};
Java
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;
}}
Python
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.totalAreaExample 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
JavaScript
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
C++ `
#include #include #include
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;}
Java
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 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());
}}
Python
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.totalAreaMain block
if name == 'main': shapes = [Circle(), Square(), Triangle()] areaCalculator = AreaCalculator() for shape in shapes: shape.accept(areaCalculator) print('Total area:', areaCalculator.getTotalArea())
` JavaScript ``
// 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.