---
layout: pattern
title: Acyclic Visitor
folder: acyclic-visitor
permalink: /patterns/acyclic-visitor/
categories: Behavioral
language: es
tag:
 - Extensibility
---

## Propósito

Permitir añadir nuevas funciones a jerarquías de clases existentes sin que estas se vean afectadas, y sin crear los problemáticos círculos de dependencias que son inherentes al patrón GoF (Gang of Four) Visitor.

## Explicación

Ejemplo del mundo real

> Tenemos una jerarquía de clases módem. Los modems de esta jerarquía deben ser visitados por un algoritmo externo basándose en unos filtros (el módem es compatible con Unix o DOS).

En otras palabras

> Acyclic Visitor permite añadir funciones a jerarquías de clases existentes sin modificarlas.

[WikiWikiWeb](https://wiki.c2.com/?AcyclicVisitor) dice

> El patrón Acyclic Visitor permite que nuevas funciones sean añadidas a jerarquías de clases existentes sin afectar a las mismas, y sin crear los círculos de dependencias que son inherentes al patrón Visitor Pattern de GangOfFour.

**Ejemplo Programático**

Aquí tenemos la jerarquía `Modem`.

```java
public abstract class Modem {
  public abstract void accept(ModemVisitor modemVisitor);
}

public class Zoom extends Modem {
  ...
  @Override
  public void accept(ModemVisitor modemVisitor) {
    if (modemVisitor instanceof ZoomVisitor) {
      ((ZoomVisitor) modemVisitor).visit(this);
    } else {
      LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
    }
  }
}

public class Hayes extends Modem {
  ...
  @Override
  public void accept(ModemVisitor modemVisitor) {
    if (modemVisitor instanceof HayesVisitor) {
      ((HayesVisitor) modemVisitor).visit(this);
    } else {
      LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
    }
  }
}
```

Después tenemos la jerarquía `ModemVisitor`.

```java
public interface ModemVisitor {
}

public interface HayesVisitor extends ModemVisitor {
  void visit(Hayes hayes);
}

public interface ZoomVisitor extends ModemVisitor {
  void visit(Zoom zoom);
}

public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
}

public class ConfigureForDosVisitor implements AllModemVisitor {
  ...
  @Override
  public void visit(Hayes hayes) {
    LOGGER.info(hayes + " used with Dos configurator.");
  }
  @Override
  public void visit(Zoom zoom) {
    LOGGER.info(zoom + " used with Dos configurator.");
  }
}

public class ConfigureForUnixVisitor implements ZoomVisitor {
  ...
  @Override
  public void visit(Zoom zoom) {
    LOGGER.info(zoom + " used with Unix configurator.");
  }
}
```

Finalmente aquí están los "visitors" en acción.

```java
    var conUnix = new ConfigureForUnixVisitor();
    var conDos = new ConfigureForDosVisitor();
    var zoom = new Zoom();
    var hayes = new Hayes();
    hayes.accept(conDos);
    zoom.accept(conDos);
    hayes.accept(conUnix);
    zoom.accept(conUnix);   
```

Output del programa:

```
    // Hayes modem used with Dos configurator.
    // Zoom modem used with Dos configurator.
    // Only HayesVisitor is allowed to visit Hayes modem
    // Zoom modem used with Unix configurator.
```

## Diagrama de clases

![alt text](../../../acyclic-visitor/etc/acyclic-visitor.png "Acyclic Visitor")

## Aplicación

Este patrón puede ser usado:

* Cuando necesitas añadir una nueva función a una jerarquía de clases sin que esta se vea afectada o alterada.
* Cuando hay funciones que operan sobre la jerarquía pero no pertenecen a la jerarquía como tal. Las clases ConfigureForDOS / ConfigureForUnix / ConfigureForX por ejemplo.
* Cuando necesitas ejecutar operaciones muy diferentes en un objeto dependiendo de su tipo.
* Cuando la jerarquía visitada va a ser frecuentemente extendida con derivados de la clase elemento.
* Cuando el proceso de volver a compilar, enlazar, probar o distribuir los derivados de la clase elemento es muy pesado.

## Tutoriales

* [Acyclic Visitor Pattern Example](https://codecrafter.blogspot.com/2012/12/the-acyclic-visitor-pattern.html)

## Consecuencias

Buenas:

* No hay círculos de dependencias entre las jerarquías.
* No es necesario compilar todos los visitantes si se añade uno nuevo.
* No provoca errores de compilación en visitantes existentes si la jerarquía tiene un nuevo miembro.

Malas:

* Viola el [Principio de sustitución de Liskov](https://java-design-patterns.com/principles/#liskov-substitution-principle) al mostrar que puede aceptar todos los visitantes solamente estando interesado en uno en particular.
* Hay que crear una jerarquía de visitantes paralela para todos los miembros de una jerarquía visitable.

## Patrones relacionados

* [Visitor Pattern](https://java-design-patterns.com/patterns/visitor/)

## Créditos

* [Acyclic Visitor by Robert C. Martin](http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/acv.pdf)
* [Acyclic Visitor in WikiWikiWeb](https://wiki.c2.com/?AcyclicVisitor)
