Introducción

El patrón de diseño Visitor es un patrón de comportamiento que permite definir nuevas operaciones sobre una estructura de objetos sin cambiar las clases de los objetos sobre los que opera. Este patrón es útil cuando se desea agregar funcionalidades a una jerarquía de clases sin modificar las clases existentes.

Conceptos Clave

  • Visitable: La interfaz o clase que acepta un visitante.
  • Visitor: La interfaz o clase que define las operaciones que se pueden realizar en los objetos visitables.
  • ConcreteVisitor: Implementaciones concretas de la interfaz Visitor que realizan operaciones específicas.
  • ConcreteElement: Implementaciones concretas de la interfaz Visitable.

Ejemplo Práctico

Supongamos que tenemos una jerarquía de clases que representan diferentes tipos de figuras geométricas y queremos agregar una operación para calcular el área de cada figura sin modificar las clases de las figuras.

Definición de Interfaces

Primero, definimos las interfaces Visitable y Visitor.

// Visitable.java
public interface Visitable {
    void accept(Visitor visitor);
}

// Visitor.java
public interface Visitor {
    void visit(Circle circle);
    void visit(Rectangle rectangle);
    void visit(Triangle triangle);
}

Clases Concretas

Luego, definimos las clases concretas que representan las figuras geométricas.

// Circle.java
public class Circle implements Visitable {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double getRadius() {
        return radius;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

// Rectangle.java
public class Rectangle implements Visitable {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    public double getWidth() {
        return width;
    }

    public double getHeight() {
        return height;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

// Triangle.java
public class Triangle implements Visitable {
    private double base;
    private double height;

    public Triangle(double base, double height) {
        this.base = base;
        this.height = height;
    }

    public double getBase() {
        return base;
    }

    public double getHeight() {
        return height;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

Implementación del Visitor

Ahora, implementamos el Visitor que calcula el área de cada figura.

// AreaVisitor.java
public class AreaVisitor implements Visitor {
    private double area;

    @Override
    public void visit(Circle circle) {
        area = Math.PI * Math.pow(circle.getRadius(), 2);
        System.out.println("Area of Circle: " + area);
    }

    @Override
    public void visit(Rectangle rectangle) {
        area = rectangle.getWidth() * rectangle.getHeight();
        System.out.println("Area of Rectangle: " + area);
    }

    @Override
    public void visit(Triangle triangle) {
        area = 0.5 * triangle.getBase() * triangle.getHeight();
        System.out.println("Area of Triangle: " + area);
    }
}

Uso del Patrón Visitor

Finalmente, utilizamos el patrón Visitor para calcular el área de diferentes figuras.

public class Main {
    public static void main(String[] args) {
        Visitable circle = new Circle(5);
        Visitable rectangle = new Rectangle(4, 6);
        Visitable triangle = new Triangle(3, 7);

        Visitor areaVisitor = new AreaVisitor();

        circle.accept(areaVisitor);
        rectangle.accept(areaVisitor);
        triangle.accept(areaVisitor);
    }
}

Ejercicio Práctico

Ejercicio

  1. Implementa una nueva clase Square que también sea visitable.
  2. Modifica el Visitor para que pueda calcular el área de un Square.
  3. Crea instancias de Square y utiliza el Visitor para calcular su área.

Solución

// Square.java
public class Square implements Visitable {
    private double side;

    public Square(double side) {
        this.side = side;
    }

    public double getSide() {
        return side;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

// Modificación en Visitor.java
public interface Visitor {
    void visit(Circle circle);
    void visit(Rectangle rectangle);
    void visit(Triangle triangle);
    void visit(Square square); // Añadido para Square
}

// Modificación en AreaVisitor.java
public class AreaVisitor implements Visitor {
    private double area;

    @Override
    public void visit(Circle circle) {
        area = Math.PI * Math.pow(circle.getRadius(), 2);
        System.out.println("Area of Circle: " + area);
    }

    @Override
    public void visit(Rectangle rectangle) {
        area = rectangle.getWidth() * rectangle.getHeight();
        System.out.println("Area of Rectangle: " + area);
    }

    @Override
    public void visit(Triangle triangle) {
        area = 0.5 * triangle.getBase() * triangle.getHeight();
        System.out.println("Area of Triangle: " + area);
    }

    @Override
    public void visit(Square square) {
        area = Math.pow(square.getSide(), 2);
        System.out.println("Area of Square: " + area);
    }
}

// Uso en Main.java
public class Main {
    public static void main(String[] args) {
        Visitable circle = new Circle(5);
        Visitable rectangle = new Rectangle(4, 6);
        Visitable triangle = new Triangle(3, 7);
        Visitable square = new Square(4); // Añadido Square

        Visitor areaVisitor = new AreaVisitor();

        circle.accept(areaVisitor);
        rectangle.accept(areaVisitor);
        triangle.accept(areaVisitor);
        square.accept(areaVisitor); // Añadido Square
    }
}

Resumen

El patrón Visitor permite agregar nuevas operaciones a una jerarquía de clases sin modificar las clases existentes. Esto es especialmente útil cuando se necesita realizar operaciones complejas en una estructura de objetos y se desea mantener el código organizado y fácil de mantener. En este módulo, hemos aprendido cómo implementar y utilizar el patrón Visitor mediante un ejemplo práctico y un ejercicio para reforzar los conceptos.

© Copyright 2024. Todos los derechos reservados