Visitor Design Pattern in Java: Streamline Object Interaction

In this article, we will do an in-depth exploration of Visitor Design Pattern in Java. So, let’s get started.

What is Visitor Design Pattern in Java ?

The Visitor Design Pattern in Java is a powerful and flexible design pattern in the realm of object-oriented programming. It allows for the separation of algorithms or operations from the objects they operate on, enabling the addition of new operations without modifying the existing object structure.

Benefits: Visitor Design Pattern in Java

The Visitor Design Pattern offers several benefits when used in Java applications:

  1. Separation of Concerns: The pattern separates the algorithm for performing operations on objects from the objects themselves. 
  2. Extensibility: The Visitor pattern allows for easy addition of new operations without modifying the existing classes. New visitor implementations can be added independently, making the system more extensible and adaptable to future changes.
  3. Open/Closed Principle: The pattern adheres to the Open/Closed Principle, which states that software entities should be open for extension but closed for modification. With the Visitor pattern, new behaviors can be added by implementing new visitor classes, without modifying the existing code.
  4. Maintainability: By encapsulating related operations within visitor classes, the code becomes more organized and maintainable. Changes to the behavior or implementation of a specific operation can be isolated within the corresponding visitor class.
  5. Reusability: The Visitor pattern promotes code reuse by allowing multiple visitors to work with the same set of objects. Different visitors can provide different operations on the objects, enabling modular and reusable code.
  6. Pattern Matching: The Visitor pattern provides a powerful mechanism for pattern matching and dispatching based on the type and structure of objects. This can be particularly useful in scenarios where complex object hierarchies and operations need to be handled.

Real World Use Cases of the Visitor Design Pattern in Java

Here are some real-world use cases of the Visitor Design Pattern in Java:

  1. Document Processing: The Visitor pattern can be used to process various elements in a document structure, such as paragraphs, images, and tables. Each element can accept a visitor, which performs specific operations based on the type of element.
  2. Compiler Design: In compiler design, the Visitor pattern is often used for traversing the abstract syntax tree (AST) and performing operations on different types of nodes. Each node type can accept a visitor, which performs actions like type checking, code generation, or optimization.
  3. GUI Components: The Visitor pattern can be used in graphical user interface (GUI) frameworks to process different types of UI components, such as buttons, checkboxes, and text fields. The visitor can perform operations like rendering, event handling, or data binding.
  4. Database Operations: When working with databases, the Visitor pattern can be applied to process different types of database objects, such as tables, views, or indexes. The visitor can perform actions like querying, updating, or deleting data based on the object type.
  5. XML/JSON Parsing: Visitor pattern can be utilized in parsing XML or JSON documents. Each element or node in the document can accept a visitor, which extracts or processes data from that element.

Components: Visitor Design Pattern in Java

Visitor Design Pattern in Java

Example: Visitor Design Pattern in Java

In the below example, we will move the logic for calculating billing and missed billed hours for employees to our visitor classes.

Class Diagram

Visitor Design Pattern in Java

Java Code

// Employee Interface

package com.design.visitor;

public interface Employee {

    public void accept(EmployeeVisitor employeeVisitor);

}

// PermanentEmployee class

package com.design.visitor;

public class PermanentEmployee implements Employee {

    private double averageHours;
    private String name;

    public double getAverageHours() {
        return averageHours;
    }

    public void setAverageHours(double averageHours) {
        this.averageHours = averageHours;
    }

    public String getName() {
        return name;
    }

    public PermanentEmployee(double averageHours, String name) {
        this.averageHours = averageHours;
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void accept(EmployeeVisitor employeeVisitor) {
        employeeVisitor.visit(this);
    }

}

// ContractualEmployee Class

package com.design.visitor;

public class ContractualEmployee implements Employee {

    private double averageHours;
    private String name;

    public double getAverageHours() {
        return averageHours;
    }

    public void setAverageHours(double averageHours) {
        this.averageHours = averageHours;
    }

    public String getName() {
        return name;
    }

    public ContractualEmployee(double averageHours, String name) {
        this.averageHours = averageHours;
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void accept(EmployeeVisitor employeeVisitor) {
        employeeVisitor.visit(this);
    }

}

// EmployeeVisitor Interface

package com.design.visitor;

public interface EmployeeVisitor {

    public void visit(PermanentEmployee permanentEmployee);
    public void visit(ContractualEmployee permanentEmployee);

}

// BillingVisitor Class

package com.design.visitor;

public class BillingVisitor implements EmployeeVisitor {

    public static double permanentEmployee = 10;
    public static double contractEmployee = 9;

    @Override
    public void visit(PermanentEmployee permanentEmployee) {
        System.out.println("Total Billing for last month for " + permanentEmployee.getName() + ":" + permanentEmployee.getAverageHours() *
            BillingVisitor.permanentEmployee * 22 + "$");
    }

    @Override
    public void visit(ContractualEmployee contractualEmployee) {
        System.out.println("Total Billing for last month for " + contractualEmployee.getName() + ":" + contractualEmployee.getAverageHours() *
            BillingVisitor.contractEmployee * 22 + "$");
    }

}

// MissingBilledHoursVisitor Class

package com.design.visitor;

public class MissingBilledHoursVisitor implements EmployeeVisitor {

    public static double permanentEmployee = 9.5;
    public static double contractEmployee = 9.0;

    @Override
    public void visit(PermanentEmployee permanentEmployee) {
        System.out.println("Missed Billing Hours for last month for " + permanentEmployee.getName() + ":" +
            (MissingBilledHoursVisitor.permanentEmployee - permanentEmployee.getAverageHours()) * 22 + " Hrs");
    }

    @Override
    public void visit(ContractualEmployee contractualEmployee) {
        System.out.println("Missed Billing Hours for last month for " + contractualEmployee.getName() + ":" + (MissingBilledHoursVisitor.contractEmployee -
            contractualEmployee.getAverageHours()) * 22 + " Hrs");
    }

}

// VisitorDemo Class

package com.design.visitor;

public class VisitorDemo {

    public static void main(String[] args) {
        EmployeeVisitor billingVisitor = new BillingVisitor();
        EmployeeVisitor missingBilledHoursVisitor = new MissingBilledHoursVisitor();
        Employee permanentEmployee = new PermanentEmployee(9, "Gyan");
        Employee contractualEmployee = new ContractualEmployee(7, "Rochit");
        permanentEmployee.accept(billingVisitor);
        contractualEmployee.accept(billingVisitor);
        permanentEmployee.accept(missingBilledHoursVisitor);
        contractualEmployee.accept(missingBilledHoursVisitor);
    }

}

// Output

Total Billing
for last month
for Gyan: 1980.0 $
Total Billing
for last month
for Rochit: 1386.0 $
Missed Billing Hours
for last month
for Gyan: 11.0 Hrs
Missed Billing Hours
for last month
for Rochit: 44.0 Hrs

Conclusion :Visitor Design Pattern in Java

Visitor Design Pattern in Java allows for the separation of operations from objects, enabling easy addition of new behaviors. By exploring its benefits, real-world applications, and practical examples, you can unlock the potential of this design pattern for enhancing modularity, extensibility, and maintainability in your Java projects.

Must Read Design Pattern Articles: 

Recommended Books:

Leave a Reply

Your email address will not be published. Required fields are marked *