El patrón Visitor
Descripción
El patrón Visitor construye una operación que debe realizarse sobre los elementos de un conjunto de objetos. Esto permite agregar nuevas operaciones sin modificar las clases de estos objetos.
Ejemplo
Consideremos la figura 4-12.1 que describe los clientes de nuestro sistema organizados bajo la forma de objetos compuestos según el patrón Composite. A excepción del método agregaFilial, específico a la gestión de la composición, las dos subclases poseen dos métodos con el mismo nombre: calculaCosteEmpresa y envíaEmailComercial. Cada uno de estos métodos se corresponde con una funcionalidad cuya implementación está bien adaptada en función de la clase. Podrían implementarse muchas otras funcionalidades como, por ejemplo, el cálculo de la cifra de negocios de un cliente (incluyendo o no sus filiales), etc.
En el diagrama, el cálculo del coste del mantenimiento no está detallado. El detalle se encuentra en el capítulo dedicado al patrón Composite.
Este enfoque puede utilizarse siempre y cuando el número de funcionalidades sea pequeño. En cambio, si se vuelve importante, obtendremos clases con muchos métodos, difíciles de comprender y de mantener. Además, estas funcionalidades darán lugar a métodos (calculaCosteEmpresa y envíaEmailComercial) sin relación entre ellos y sin relación entre el núcleo de los objetos con la diferencia, por ejemplo, del método agregaFilial que contribuye a componer los objetos.
Figura 4-12.1 - Múltiples funcionalidades...
Estructura
1. Diagrama de clases
La figura 4-12.3 detalla la estructura genérica del patrón.
Figura 4-12.3 - Estructura del patrón Visitor
2. Participantes
Los participantes del patrón son los siguientes:
-
Visitante es la interfaz que incluye la firma de los métodos que realizan una funcionalidad en un conjunto de clases. Existe un método para cada clase que recibe como argumento una instancia de esta clase.
-
VisitanteConcreto1 y VisitanteConcreto2 (VisitanteCálculoCosteEmpresa y VisitanteMailingComercial) implementan los métodos que realizan la funcionalidad correspondiente a la clase.
-
Elemento (Empresa) es una clase abstracta súperclase de las clases de elementos. Incluye el método abstracto aceptaVisitante que acepta un visitante como argumento.
-
ElementoConcreto1 y ElementoConcreto2 (EmpresaSinFilial y EmpresaMadre) implementa el método aceptaVisitante que consiste en volver a llamar al visitante a través del método correspondiente de la clase.
3. Colaboraciones
Un cliente que utiliza un visitante debe en primer lugar crearlo como instancia de la clase de su elección y, a continuación, pasarlo como argumento al método aceptaVisitante de un conjunto de elementos.
El elemento vuelve a llamar al método del visitante que corresponde con su clase. Le pasa una referencia hacia sí mismo como argumento para que el visitante pueda acceder a su estructura...
Dominios de aplicación
El patrón se utiliza en los casos siguientes:
-
Es necesario agregar numerosas funcionalidades a un conjunto de clases sin volverlas pesadas.
-
Un conjunto de clases poseen una estructura fija y es necesario agregarles funcionalidades sin modificar su interfaz.
Si la estructura del conjunto de clases a las que es necesario agregar funcionalidades cambia a menudo, el patrón Visitor no se adapta bien. En efecto, cualquier modificación de la estructura implica una modificación de cada visitante, lo cual puede tener un coste elevado.
Ejemplo en Java
Retomamos el ejemplo de la figura 4-12.2. A continuación se muestra el código de la clase Empresa escrita en Java. El método aceptaVisitante es abstracto pues su código depende de la subclase.
public abstract class Empresa
{
protected String nombre, email, direccion;
public Empresa(String nombre, String email, String direccion)
{
this.setNombre(nombre);
this.setEmail(email);
this.setDireccion(direccion);
}
public String getNombre()
{
return nombre;
}
protected void setNombre(String nombre)
{
this.nombre = nombre;
}
public String getEmail()
{
return email;
}
protected void setEmail(String email)
{
this.email = email;
}
public String getDireccion()
{
return direccion;
}
protected void setDireccion(String direccion)
{ ...