Open In App

Visitor Method Design Patterns in C++

A visitor design patterns or visitor method is basically defined as a behavioral design pattern that allows us to define a new operation without changing the classes of the elements on which it operates.



It is particularly useful when we have a set of related classes, and we want to perform different operations on each class without modifying their code.

Use Cases of the Visitor Design Patterns in C++ Design Patterns

Example for Visitor Design Patterns in C++:

Problem Statement:

Let’s consider a simple example of the Visitor Pattern in C++ involving a set of geometric shapes (elements) and a set of operations (visitors) that can be performed on these shapes.

Step wise implementation of Visitor Design Patterns in C++:

Visitor Interface




class Visitor {
public:
    virtual void visit(ElementA& element) = 0;
    virtual void visit(ElementB& element) = 0;
    // ... other visit methods for different elements
};

Concrete Visitor




class ConcreteVisitor : public Visitor {
public:
    void visit(ElementA& element) override {
        // Perform operation on ElementA
    }
 
    void visit(ElementB& element) override {
        // Perform operation on ElementB
    }
    // ... implementations for other visit methods
};

Element Interface




class Element {
public:
    virtual void accept(Visitor& visitor) = 0;
};

Concrete Element




class ElementA : public Element {
public:
    void accept(Visitor& visitor) override {
        visitor.visit(*this);
    }
};
 
class ElementB : public Element {
public:
    void accept(Visitor& visitor) override {
        visitor.visit(*this);
    }
};

Object Structure

overall




#include <iostream>
#include <vector>
 
// Forward declarations
class Circle;
class Square;
 
// Visitor interface
class ShapeVisitor {
public:
    virtual void visit(Circle& circle) = 0;
    virtual void visit(Square& square) = 0;
};
 
// Element interface
class Shape {
public:
    virtual void accept(ShapeVisitor& visitor) = 0;
};
 
// Concrete Element: Circle
class Circle : public Shape {
public:
    void accept(ShapeVisitor& visitor) override {
        visitor.visit(*this);
    }
 
    void draw() {
        std::cout << "Drawing Circle\n";
    }
};
 
// Concrete Element: Square
class Square : public Shape {
public:
    void accept(ShapeVisitor& visitor) override {
        visitor.visit(*this);
    }
 
    void draw() {
        std::cout << "Drawing Square\n";
    }
};
 
// Concrete Visitor: DrawingVisitor
class DrawingVisitor : public ShapeVisitor {
public:
    void visit(Circle& circle) override {
        std::cout << "Drawing a Circle\n";
        circle.draw();
    }
 
    void visit(Square& square) override {
        std::cout << "Drawing a Square\n";
        square.draw();
    }
};
 
// Concrete Visitor: AreaVisitor
class AreaVisitor : public ShapeVisitor {
public:
    void visit(Circle& circle) override {
        std::cout << "Calculating area of Circle\n";
        // Calculate and print area logic for Circle
    }
 
    void visit(Square& square) override {
        std::cout << "Calculating area of Square\n";
        // Calculate and print area logic for Square
    }
};
 
// Object Structure
class ShapeContainer {
public:
    void addShape(Shape* shape) {
        shapes.push_back(shape);
    }
 
    void performOperations(ShapeVisitor& visitor) {
        for (Shape* shape : shapes) {
            shape->accept(visitor);
        }
    }
 
private:
    std::vector<Shape*> shapes;
};
 
int main() {
    // Create instances of shapes
    Circle circle;
    Square square;
 
    // Create a container and add shapes to it
    ShapeContainer container;
    container.addShape(&circle);
    container.addShape(&square);
 
    // Create visitors
    DrawingVisitor drawingVisitor;
    AreaVisitor areaVisitor;
 
    // Perform drawing operations
    container.performOperations(drawingVisitor);
 
    // Perform area calculation operations
    container.performOperations(areaVisitor);
 
    return 0;
}

Output
Drawing a Circle
Drawing Circle
Drawing a Square
Drawing Square
Calculating area of Circle
Calculating area of Square






Explanation of example:

Diagrammatic Representation of Visitor Pattern in C++

The Visitor Pattern is particularly useful when the object structure is complex, and the algorithm to be applied to its elements is likely to change or be extended. It allows for adding new operations without modifying the existing code for the elements or the object structure.

Advantages of Visitor Design Patterns in C++

Disadvantages of Visitor Design Patterns in C++

Conclusion

Visitor pattern provides a powerful mechanism for separating algorithms from the objects on which they operate, it should be applied judiciously. It is most beneficial in situations where the benefits of extensibility and separation of concerns outweigh the added complexity and potential drawbacks.


Article Tags :