El patrón Visitor es un patrón de diseño de comportamiento que permite separar algoritmos de los objetos sobre los que operan. Este patrón proporciona una manera de agregar nuevas operaciones a un conjunto de objetos sin modificar las clases de estos objetos. Se usa principalmente cuando tienes un conjunto de clases que forman parte de una estructura de datos y quieres realizar distintas operaciones sobre estos sin cambiar su implementación.
En este artículo, exploraremos cómo funciona el patrón Visitor, cuándo utilizarlo y cómo implementarlo en PHP. Acompañaremos la explicación con ejemplos prácticos para facilitar la comprensión.
El patrón Visitor es un patrón de comportamiento que permite definir nuevas operaciones en una jerarquía de clases sin modificarlas. Lo hace mediante la creación de un objeto *visitor* que contiene todas las operaciones que pueden realizarse sobre los elementos de la estructura.
Imagina que tienes una colección de diferentes tipos de objetos y quieres realizar operaciones específicas según el tipo de objeto. Implementar todas estas operaciones dentro de las propias clases puede hacer que la jerarquía sea difícil de mantener. El patrón Visitor proporciona una solución a este problema, al separar estas operaciones en clases *visitor* independientes.
El patrón Visitor consta de los siguientes componentes principales:
1. Visitor: Interfaz o clase abstracta que declara métodos para cada tipo de elemento visitable.
2. ConcreteVisitor: Implementación concreta del Visitor
que define operaciones específicas para cada tipo de elemento.
3. Element: Interfaz o clase abstracta que declara un método accept
que recibe un visitante.
4. ConcreteElement: Implementación de Element
que implementa el método accept
y llama al método correspondiente del visitor.
5. Client: Parte del código que inicializa los elementos y visitantes, y coordina las llamadas de los métodos.
El patrón Visitor es útil en las siguientes situaciones:
Para ilustrar cómo se implementa el patrón Visitor en PHP, usaremos un ejemplo de una jerarquía de formas geométricas (Círculo, Rectángulo, y Triángulo) sobre las cuales queremos realizar distintas operaciones (como cálculo de área y perímetro).
Visitor
Primero, creamos la interfaz Visitor
que define métodos para cada tipo de elemento que puede ser visitado.
<?php
interface Visitor {
public function visitCircle(Circle $element);
public function visitRectangle(Rectangle $element);
public function visitTriangle(Triangle $element);
}
?>
Element
A continuación, definimos la interfaz Element
que declara un método accept
que toma como parámetro un objeto del tipo Visitor
.
<?php
interface Element {
public function accept(Visitor $visitor);
}
?>
ConcreteElement
Creamos las clases concretas que implementan la interfaz Element
. En este caso, tenemos tres formas: Circle
, Rectangle
y Triangle
. Cada clase concreta implementa el método accept
de la interfaz Element
.
<?php
class Circle implements Element {
private $radius;
public function __construct($radius) {
$this->radius = $radius;
}
public function getRadius() {
return $this->radius;
}
public function accept(Visitor $visitor) {
$visitor->visitCircle($this);
}
}
class Rectangle implements Element {
private $width;
private $height;
public function __construct($width, $height) {
$this->width = $width;
$this->height = $height;
}
public function getWidth() {
return $this->width;
}
public function getHeight() {
return $this->height;
}
public function accept(Visitor $visitor) {
$visitor->visitRectangle($this);
}
}
class Triangle implements Element {
private $base;
private $height;
public function __construct($base, $height) {
$this->base = $base;
$this->height = $height;
}
public function getBase() {
return $this->base;
}
public function getHeight() {
return $this->height;
}
public function accept(Visitor $visitor) {
$visitor->visitTriangle($this);
}
}
?>
ConcreteVisitor
Ahora creamos un ConcreteVisitor
que implementa la interfaz Visitor
y define el comportamiento específico para cada tipo de objeto. En este ejemplo, nuestro visitante calculará el área de las diferentes formas geométricas.
<?php
class AreaVisitor implements Visitor {
public function visitCircle(Circle $circle) {
$area = pi() * pow($circle->getRadius(), 2);
echo "Área del Círculo: " . $area . PHP_EOL;
}
public function visitRectangle(Rectangle $rectangle) {
$area = $rectangle->getWidth() * $rectangle->getHeight();
echo "Área del Rectángulo: " . $area . PHP_EOL;
}
public function visitTriangle(Triangle $triangle) {
$area = 0.5 * $triangle->getBase() * $triangle->getHeight();
echo "Área del Triángulo: " . $area . PHP_EOL;
}
}
?>
Finalmente, creamos instancias de las formas geométricas y aplicamos el visitante AreaVisitor
a cada una de ellas.
<?php
// Crear instancias de formas geométricas
$circle = new Circle(5);
$rectangle = new Rectangle(4, 6);
$triangle = new Triangle(4, 7);
// Crear un visitor para calcular el área
$areaVisitor = new AreaVisitor();
// Aplicar el visitor a cada forma
$circle->accept($areaVisitor);
$rectangle->accept($areaVisitor);
$triangle->accept($areaVisitor);
?>
Área del Círculo: 78.539816339745
Área del Rectángulo: 24
Área del Triángulo: 14
1. Separación de responsabilidades: El patrón Visitor permite separar el algoritmo de la estructura de datos.
2. Facilidad para agregar operaciones: Se pueden añadir nuevas operaciones creando nuevas clases de visitantes sin modificar las clases de los elementos.
3. Mejor mantenimiento: El código de las operaciones se mantiene en un solo lugar, lo que facilita el mantenimiento.
1. Dificultad para agregar nuevos elementos: Si la estructura de la jerarquía cambia frecuentemente, el patrón Visitor no es adecuado, ya que agregar un nuevo tipo de elemento requerirá modificar todos los visitantes existentes.
2. Alta dependencia: Los visitantes dependen de la estructura interna de los elementos, lo que puede romper el principio de encapsulamiento.
El patrón Visitor es una poderosa herramienta para separar las operaciones de las estructuras de datos y facilita la adición de nuevas funcionalidades a una jerarquía existente. En PHP, se implementa fácilmente utilizando interfaces y clases concretas que definen los distintos visitantes y elementos. Sin embargo, hay que tener en cuenta las limitaciones del patrón, como la dificultad de mantenerlo si se modifica la estructura de los elementos con frecuencia.
Con esto concluye la explicación del patrón Visitor en PHP. Si necesitas trabajar con estructuras complejas y agregar múltiples operaciones a tus clases, ¡este patrón puede ser la solución que buscas!
Jorge García
Fullstack developer