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
- Implementa una nueva clase
Square
que también sea visitable. - Modifica el
Visitor
para que pueda calcular el área de unSquare
. - Crea instancias de
Square
y utiliza elVisitor
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.
Curso de Patrones de Diseño de Software
Módulo 1: Introducción a los Patrones de Diseño
- ¿Qué son los Patrones de Diseño?
- Historia y Origen de los Patrones de Diseño
- Clasificación de los Patrones de Diseño
- Ventajas y Desventajas de Usar Patrones de Diseño
Módulo 2: Patrones Creacionales
- Introducción a los Patrones Creacionales
- Singleton
- Factory Method
- Abstract Factory
- Builder
- Prototype
Módulo 3: Patrones Estructurales
Módulo 4: Patrones de Comportamiento
- Introducción a los Patrones de Comportamiento
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
Módulo 5: Aplicación de Patrones de Diseño
- Cómo Seleccionar el Patrón Adecuado
- Ejemplos Prácticos de Uso de Patrones
- Patrones de Diseño en Proyectos Reales
- Refactorización Usando Patrones de Diseño
Módulo 6: Patrones de Diseño Avanzados
- Patrones de Diseño en Arquitecturas Modernas
- Patrones de Diseño en Microservicios
- Patrones de Diseño en Sistemas Distribuidos
- Patrones de Diseño en Desarrollo Ágil