La Pro­gra­ma­ción Orientada a Objetos (OOP), que se clasifica como una su­b­ca­te­go­ría del paradigma de pro­gra­ma­ción im­pe­ra­ti­va, ha ganado mucha im­po­r­ta­n­cia en los últimos años. Este sistema, en el que todos los bloques de un proyecto de software se describen como objetos cuyo co­m­po­r­ta­mie­n­to es de­te­r­mi­na­do por las clases co­rre­s­po­n­die­n­tes, posee algunas ventajas decisivas sobre otros estilos de pro­gra­ma­ción. Uno de los ar­gu­me­n­tos a su favor es que permite re­uti­li­zar fá­ci­l­me­n­te las partes del programa ya diseñadas.

Para que esta re­uti­li­za­ción, así como la im­ple­me­n­ta­ción, ada­p­ta­bi­li­dad y co­m­pro­ba­bi­li­dad de los objetos in­co­r­po­ra­dos sea aún más sencilla, el grupo de de­sa­rro­lla­do­res software “Gang of four” presentó sus patrones de diseño en el libro Patrones de Diseño: elementos del software re­uti­li­za­ble orientado a objetos. Uno de los más de 20 patrones de diseño di­fe­re­n­tes descritos en el libro es el llamado visitor pattern, que de­s­cri­bi­mos en detalle en este artículo.

¿En qué consiste el visitor pattern?

El visitor pattern es un patrón de solución para separar un algoritmo de la es­tru­c­tu­ra del objeto en el que se ejecuta. Describe una forma de añadir nuevas ope­ra­cio­nes a las es­tru­c­tu­ras de los objetos exi­s­te­n­tes sin modificar dichas es­tru­c­tu­ras. Gracias a esta propiedad, el visitor pattern es una manera de im­ple­me­n­tar el Principio Abierto-Cerrado (OCP). Este principio de de­sa­rro­llo de software, orientado a objetos, se basa en el hecho de que todas las unidades de software, como los módulos, clases o métodos, están si­mu­l­tá­nea­me­n­te abiertas para ex­te­n­sio­nes y cerradas para mo­di­fi­ca­cio­nes.

Nota

En ca­s­te­llano, el visitor pattern se llama también patrón visitor.

El visitor pattern es uno de los 23 patrones de diseño, en la categoría de los patrones de co­m­po­r­ta­mie­n­to, descritos y pu­bli­ca­dos en 1994 por los in­fo­r­má­ti­cos Erich Gamma, Richard Helm, Ralph Johnson y John Vlissides. Dado que los cuatro son también conocidos en la escena de los de­sa­rro­lla­do­res como Gang of Four o GoF para abreviar, a estos patrones también se los llama diseños de patrón GoF.

¿Para qué sirve el visitor design pattern?

Puesto que la es­tru­c­tu­ra de los objetos está compuesta de muchas clases inconexas y requiere cada vez más ope­ra­cio­nes, para los de­sa­rro­lla­do­res es muy in­co­n­ve­nie­n­te im­ple­me­n­tar una nueva subclase para cada nueva operación. El resultado es un sistema plagado con varias clases de nodos di­fe­re­n­tes que no solo es difícil de entender, sino también de mantener y modificar. La instancia esencial del visitor pattern es el Visitor, que permite añadir nuevas funciones virtuales a una familia de clases sin mo­di­fi­car­las.

Nota

Las funciones o métodos virtuales definen las funciones que se deben ejecutar, cuyo objetivo no tiene que ser conocido en el momento de la co­m­pi­la­ción. Estas funciones re­pre­se­n­tan una im­po­r­ta­n­te he­rra­mie­n­ta de los lenguajes orie­n­ta­dos a los objetos.

El visitor pattern establece que el objeto Visitor se define por separado y tiene el fin de im­ple­me­n­tar una operación que se realiza en uno o más elementos de la es­tru­c­tu­ra del objeto. Los clientes que acceden a la es­tru­c­tu­ra del objeto llaman entonces a la operación de envió accept(visitor) en el elemento en cuestión, lo que delega la petición en el objeto visitante aceptado. Así, el objeto Visitor puede realizar la operación necesaria.

Re­pre­se­n­ta­ción gráfica del visitor pattern (diagrama UML)

La in­ter­ac­ción entre los elementos exi­s­te­n­tes y los objetos Visitor, in­te­gra­dos según el visitor pattern, se puede ilustrar mejor mediante una re­pre­se­n­ta­ción gráfica de las re­la­cio­nes y procesos de un software de ejemplo orientado a objetos. El método de re­pre­se­n­ta­ción más apropiado para esto es el UML (Unified Modeling Language), que, por este motivo, se ha utilizado también en el siguiente diagrama de clases para el visitor pattern.

Ventajas e in­co­n­ve­nie­n­tes del visitor pattern

El visitor pattern es una forma ya de por sí so­fi­s­ti­ca­da y elaborada de ampliar las unidades exi­s­te­n­tes de un software orientado a objetos. Si hay que añadir una nueva operación, se puede hacer con facilidad añadiendo un nuevo Visitor. Además, esta es­tra­te­gia también permite ce­n­tra­li­zar cualquier código funcional: la ejecución re­s­pe­c­ti­va de una operación está ce­n­tra­li­za­da en la clase Visitor y no es necesario añadirla a las otras clases. La ventaja más im­po­r­ta­n­te del software basado en el visitor pattern es, en resumen, que el código fuente su­b­ya­ce­n­te de los objetos uti­li­za­dos no necesita pasar por mo­di­fi­ca­cio­nes co­n­s­ta­n­tes. En su lugar, la lógica se tra­n­s­fie­re a los Visitors y a las clases Visitor que actúan como su­s­ti­tu­tos.

Na­tu­ra­l­me­n­te, sin embargo, el visitor pattern no es la solución ideal en todos los casos. Si trabajas según los pri­n­ci­pios de este patrón, debes ser co­n­s­cie­n­te de lo siguiente: los cambios más pequeños en la clase de un elemento suelen requerir ada­p­ta­cio­nes de las clases Visitor. Además, se requiere un trabajo adicional para in­tro­du­cir po­s­te­rio­r­me­n­te nuevos elementos, ya que también requieren la apli­ca­ción de métodos de visit(), que deben añadirse también a las clases Co­n­cre­te­Vi­si­tor. Por lo tanto, la excelente capacidad de am­plia­ción de las unidades de software se paga con un cierto grado de esfuerzo.

¿Para qué se utiliza el visitor pattern?

El visitor pattern puede si­m­pli­fi­car co­n­si­de­ra­ble­me­n­te las tareas re­cu­rre­n­tes en el de­sa­rro­llo de software. Este patrón de diseño es apto sobre todo para los de­sa­rro­lla­do­res que siguen el paradigma de la pro­gra­ma­ción orientada a objetos. Desde su in­tro­du­c­ción en 1994, el patrón se ha vuelto ubicuo en la escena de la pro­gra­ma­ción. En principio, el tipo de proyecto de software no es lo im­po­r­ta­n­te a la hora de decidir la utilidad del patrón. Tampoco hay re­s­tri­c­cio­nes concretas con respecto a los lenguajes de pro­gra­ma­ción uti­li­za­dos, siempre y cuando se recuerde que funciona mejor en los pa­ra­di­g­mas orie­n­ta­dos a objetos.

Entre los lenguajes populares en los que el visitor pattern tiene un papel esencial, están los si­guie­n­tes:

  • C++
  • C#
  • Java
  • PHP
  • Python
  • Ja­va­S­cri­pt
  • Golang
iueor-qViR4.jpg Para mostrar este video, se requieren cookies de terceros. Puede acceder y cambiar sus ajustes de cookies aquí.

Ejemplo práctico para el uso del visitor pattern

La utilidad y el propósito del visitor pattern no son tan sencillos de entender. Sin embargo, cua­l­quie­ra que aprenda a programar hoy en día se­gu­ra­me­n­te entrará en contacto con este patrón y su im­ple­me­n­ta­ción.

Para entender el visitor pattern, a menudo se utiliza la analogía de un trayecto en taxi. Un cliente pide un taxi, que llegará a su puerta. Una vez que la persona se sienta en el taxi, de visita, el taxi (o el conductor) tiene el control total del tra­n­s­po­r­te de la persona.

Las compras en un su­pe­r­me­r­ca­do también se utilizan a menudo como ejemplo de un visitor pattern. El comprador recoge los bienes deseados en el carrito de la compra, que re­pre­se­n­ta vi­sua­l­me­n­te el conjunto de elementos de la es­tru­c­tu­ra del objeto. Cuando llega a la caja, el cajero actúa como un visitante que escanea los precios y el peso de los distintos artículos (o elementos) de la compra para calcular los costes totales in­cu­rri­dos.

Ejemplo de código según el visitor pattern (PHP)

Fi­na­l­me­n­te, el siguiente código ilustra una im­ple­me­n­ta­ción simple y básica del visitor pattern en PHP.

return 'B';
	}
	public function getData() {
		return $this->the_data;
	}
	recibir public function (Visitante $visitante) {
		$visitante->VisitaDeElementB($this);
	}
}
abstract class Visitante {
	abstract function VisitaDeElementA(ElementA $elem);
	abstract function VisitaDeElementB(ElementB $elem);
}
class Visitante1 extends Visitante {
	private $characteristics;
	public function getCharacs() {
		return $this->characteristics;
	}
	public function VisitaDeElementA(ElementA $elem) {
		$this->characteristics = 'Info:'.$elem->getInfo();
	}
	public function VisitaDeElementB(ElementB $elem) {
		$this->characteristics = 'DATA:'.$elem->getData().'!!';
	}
}
function Test() {
	write_line('Inicio del test');
	// Estructura del objeto
	$elemente = array (
		new ElementA('Hola', 'Nuevo!!'),
		new ElementB('Por fin')
	);
	$bes1 = new Visitante1();
	foreach ($elemente as $element) {
		$element->aceptar($bes1);
		write_line('Tras visitar Element '.$element->getName().': '.$bes1-			>getCharacs());
}
}
function write_line($text) {
	print $text.'<br>';
}
Test();

La salida de este fragmento de código de ejemplo tiene la siguiente forma:

Inicio del test
Después de visitar Element A: Información: [Hola, Nuevo!!]
Después de visitar Element B: DATA:(Por fin.)!!
Ir al menú principal