Open In App

Visitor design pattern

Last Updated : 03 Jan, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

An object-oriented programming method called the Visitor design pattern makes it possible to add new operations to preexisting classes without changing them. It improves the modularity and maintainability of code, which makes it perfect for operations on a variety of object structures. Its advantages, and real-world applications are examined in this article.

What is Visitor Design Pattern?

The Visitor design pattern is a behavioral pattern that allows you to add new operations to a group of related classes without modifying their structures. It is particularly useful when you have a stable set of classes but need to perform various operations on them, making it easy to extend functionality without altering the existing codebase.

Real-World Example of Visitor Design Pattern

A simple example of the Visitor design pattern is in an online shopping cart. Imagine you have different items like books, electronics, and clothing. Each item can accept a visitor that performs actions like calculating the total price or applying discounts. This way, you can add new features without changing the item classes, making the system easier to maintain and update.

UML Class Diagram of Visitor design pattern

Visitor-Design-Pattern-Diagram

Components of Visitor Design Pattern

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 accept method 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.

How to Implement Visitor Design Pattern?

The Visitor design pattern works by separating an algorithm from the objects on which it operates, allowing you to add new operations without changing the existing object structures. Below is how it functions:

  • Step 1: First, you define an interface for the elements (objects) that will accept visitors. This interface usually includes a method for accepting a visitor.
  • Step 2: Then, you create concrete classes that implement the element interface. Each class represents an object in your structure and includes the method to accept a visitor.
  • Step 3: Next, you define a visitor interface that declares a visit method for each concrete element type. This method will be called when a visitor visits a specific element.
  • Step 4: You implement one or more concrete visitor classes, each providing specific operations for the various element types. Each visit method contains the logic for what to do when visiting that element.
  • Step 5: When a visitor is applied to an element, the element calls the appropriate visit method on the visitor, passing itself as an argument. This allows the visitor to operate on the element and perform the desired actions.

Example of Visitor Design Pattern

Below is the problem statement to understand visitor design pattern:

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:

Java
public interface ShapeVisitor {
    void visit(Circle circle);
    void visit(Square square);
    void visit(Triangle triangle);
}

Step 2: Define the Element interface:

Java
public interface Shape {
    void accept(ShapeVisitor visitor);
}

Step 3: Implement Concrete Elements:

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);
    }
}

Step 4: Implement Concrete Visitors:

Java
public class AreaCalculator implements ShapeVisitor {
    double totalArea = 0;

    @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;
    }
}

Complete Code of Visitor Design Pattern

Below is the overall code of the above example:

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<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());
    }
}
Output
Total area: 103.53981

Pros of Visitor Design Pattern

Below are the Pros of Visitor Design Pattern:

  • 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.

Cons of Visitor Design Pattern

Below are the Pros of Visitor Design Pattern:

  • 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.



Next Article
Practice Tags :

Similar Reads