Skip to content

Latest commit

 

History

History
139 lines (104 loc) · 5.96 KB

3K. Visitor.md

File metadata and controls

139 lines (104 loc) · 5.96 KB

Visitor

The Visitor design pattern is used to separate the algorithm or operations from the objects on which it operates. It allows adding new operations to existing object structures without modifying those structures. In this pattern, the operations are encapsulated in visitor objects, which can visit different types of objects and perform specific operations on them.

image

// Visitor Interface
interface VehicleVisitor {
    void visitCar(Car car);
    void visitTruck(Truck truck);
    void visitMotorcycle(Motorcycle motorcycle);
}

// Concrete Visitor
class FuelConsumptionVisitor implements VehicleVisitor {
    @Override
    public void visitCar(Car car) {
        // Calculate fuel consumption for a car
        System.out.println("Calculating fuel consumption for a car");
    }

    @Override
    public void visitTruck(Truck truck) {
        // Calculate fuel consumption for a truck
        System.out.println("Calculating fuel consumption for a truck");
    }

    @Override
    public void visitMotorcycle(Motorcycle motorcycle) {
        // Calculate fuel consumption for a motorcycle
        System.out.println("Calculating fuel consumption for a motorcycle");
    }
}

// Element Interface
interface Vehicle {
    void accept(VehicleVisitor visitor);
}

// Concrete vehicle classes
class Car implements Vehicle {
    // Car-specific attributes and methods

    @Override
    public void accept(VehicleVisitor visitor) {
        visitor.visitCar(this);
    }
}

// Concrete Element
class Truck implements Vehicle {
    // Truck-specific attributes and methods

    @Override
    public void accept(VehicleVisitor visitor) {
        visitor.visitTruck(this);
    }
}

// Concrete Element
class Motorcycle implements Vehicle {
    // Motorcycle-specific attributes and methods

    @Override
    public void accept(VehicleVisitor visitor) {
        visitor.visitMotorcycle(this);
    }
}

// Client
public class Main {
    public static void main(String[] args) {
        Vehicle[] vehicles = {new Car(), new Truck(), new Motorcycle()};

        FuelConsumptionVisitor fuelVisitor = new FuelConsumptionVisitor();

        for (Vehicle vehicle : vehicles) {
            vehicle.accept(fuelVisitor);
        }
    }
}

In this example, we have the Vehicle interface, which all concrete vehicle classes (Car, Truck, Motorcycle) implement. The Vehicle interface defines the accept method, which accepts a visitor as a parameter.

The VehicleVisitor interface defines the visitor's methods for each type of vehicle. In this case, we have visitCar, visitTruck, and visitMotorcycle methods. The FuelConsumptionVisitor is a concrete visitor class that implements the VehicleVisitor interface. It provides specific implementations for calculating fuel consumption for each type of vehicle.

In the main function, we create an array of Vehicle objects representing different types of vehicles. We also create an instance of the FuelConsumptionVisitor. We iterate over the vehicles and call the accept method on each vehicle, passing the visitor as a parameter. This allows the visitor to perform the appropriate operation for each type of vehicle.

By using the Visitor pattern, we separate the fuel consumption calculation logic from the vehicle classes themselves. The visitor encapsulates the operations that can be performed on each type of vehicle, and the vehicles accept the visitor, allowing it to perform the necessary calculations or operations.

This pattern provides a flexible and extensible way to define new operations on a set of related classes without modifying their individual implementations. It allows you to add new operations by simply implementing a new visitor class (say EngineVisitor), rather than modifying the existing classes (Car, Truck, Motorcycle). This promotes separation of concerns and makes it easier to maintain and extend the codebase in the future.

Visitor Design Pattern Summary

Use the Visitor when you need to perform an operation on all elements of a complex object structure (for example, an object tree).

  • The complex object structure in the example can be thought of as the set of vehicles (Car, Truck, Motorcycle). By implementing the Visitor pattern, we can easily perform an operation (such as calculating fuel consumption) on all vehicles in the object structure.

Use the Visitor to clean up the business logic of auxiliary behaviors.

  • Visitor pattern allows you to delegate or separate the auxiliary behaviors, such as calculating fuel consumption, to the Visitor class via the accept() method.

Use the pattern when a behavior makes sense only in some classes of a class hierarchy, but not in others.

  • We can extract this behavior into a separate visitor class and implement only those visiting methods that accept objects of relevant classes, leaving the rest empty.

    • We would need a CargoCapacityVisitor class that implements the VehicleVisitor interface. The CargoCapacityVisitor would only implement the visitTruck method to calculate and handle the cargo capacity for Truck objects. For the remaining visiting methods, such as visitCar and visitMotorcycle, which are not applicable for cargo capacity calculation, we can leave them empty or provide default behavior.
class CargoCapacityVisitor implements VehicleVisitor {

    @Override
    public void visitCar(Car car) {

    }

    @Override
    public void visitTruck(Truck truck) {
        System.out.println("Calculating cargo capacity for a truck");
    }

    @Override
    public void visitMotorcycle(Motorcycle motorcycle) {

    }
}
public class Main {
    public static void main(String[] args) {
        Vehicle truck = new Truck();
        truck.accept(new CargoCapacityVisitor()); // Output: Calculating cargo capacity for a truck

        Vehicle car = new Car();
        car.accept(new CargoCapacityVisitor()); // No Output
    }
}