---
title: "Visitor y doble despacho"
type: "design-pattern"
slug: "visitor-double-dispatch"
url: "http://localhost:3000/es/design-patterns/visitor-double-dispatch.md"
---
# Visitor y doble despacho

Echemos un vistazo a la siguiente jerarquía de clases de formas geométricas (cuidado, es pseudocódigo):

interface Graphic is
    method draw()

class Shape implements Graphic is
    field id
    method draw()
    // ...

class Dot extends Shape is
    field x, y
    method draw()
    // ...

class Circle extends Dot is
    field radius
    method draw()
    // ...

class Rectangle extends Shape is
    field width, height
    method draw()
    // ...

class CompoundGraphic implements Graphic is
    field children: array of Graphic
    method draw()
    // ...

El código funciona bien y la aplicación está en producción. Pero un día decides crear una función de exportación. El código de exportación quedaría fuera de lugar si se colocara en estas clases. Así que, en lugar de añadir la exportación a todas las clases de esta jerarquía, decides crear una nueva clase, externa a la jerarquía, y poner toda la lógica de exportación dentro. La clase tendría métodos para exportar el estado público de cada objeto a cadenas XML:

class Exporter is
    method export(s: Shape) is
        print("Exporting shape")
    method export(d: Dot)
        print("Exporting dot")
    method export(c: Circle)
        print("Exporting circle")
    method export(r: Rectangle)
        print("Exporting rectangle")
    method export(cs: CompoundGraphic)
        print("Exporting compound")

El código tiene buena pinta, pero probémoslo:

class App() is
    method export(shape: Shape) is
        Exporter exporter = new Exporter()
        exporter.export(shape);

app.export(new Circle());
// Por desgracia, esto imprimirá "Exporting shape".

¡Espera! ¿¡Por qué!?

## Pensar como un compilador

Nota: la información siguiente es cierta para la mayoría de los lenguajes de programación orientados a objetos modernos (Java, C#, PHP y otros).

### Enlace tardío/dinámico

Imagina que eres un compilador. Tienes que decidir cómo compilar el siguiente código:

method drawShape(shape: Shape) is
    shape.draw();

Veamos... el método `draw` está definido en la clase `Shape`. Un momento, pero también hay cuatro subclases que sobrescriben este método. ¿Podemos decidir con seguridad cuál de las implementaciones llamar aquí? No lo parece. La única forma de saberlo con certeza es lanzar el programa y comprobar la clase del objeto que se pasa al método. Lo único que sabemos con seguridad es que el objeto **tendrá** una implementación del método `draw`.

Así que el código máquina resultante comprobará la clase del objeto pasado al parámetro `shape` y elegirá la implementación de `draw` de la clase correspondiente.

Esta comprobación de tipo dinámica se denomina enlace tardío (o dinámico):

* **Tardío**, porque vinculamos el objeto y su implementación después de la compilación, en tiempo de ejecución.
* **Dinámico**, porque cada nuevo objeto podría necesitar vincularse a una implementación diferente.

### Enlace temprano/estático

Ahora, «compilemos» el siguiente código:

method exportShape(shape: Shape) is
    Exporter exporter = new Exporter()
    exporter.export(shape);

Todo está claro con la segunda línea: la clase `Exporter` no tiene un constructor personalizado, así que simplemente instanciamos un objeto. ¿Y la llamada a `export`? La clase `Exporter` tiene cinco métodos con el mismo nombre que difieren en los tipos de sus parámetros. ¿Cuál llamar? Parece que aquí también vamos a necesitar un enlace dinámico.

Pero hay otro problema. ¿Qué pasa si existe una clase de forma que no tiene el método `export` apropiado en la clase `Exporter`? Por ejemplo, un objeto `Ellipse`. El compilador no puede garantizar que el método sobrecargado apropiado exista, a diferencia de lo que ocurre con los métodos sobrescritos. Surge una situación ambigua que un compilador no puede permitir.

Por lo tanto, los desarrolladores de compiladores optan por una vía segura y usan el enlace temprano (o estático) para los métodos sobrecargados:

* **Temprano**, porque ocurre en tiempo de compilación, antes de que se lance el programa.
* **Estático**, porque no se puede alterar en tiempo de ejecución.

Volvamos a nuestro ejemplo. Estamos seguros de que el argumento entrante será de la jerarquía de `Shape`: o bien la clase `Shape`, o bien una de sus subclases. También sabemos que la clase `Exporter` tiene una implementación básica de la exportación que admite la clase `Shape`: `export(s: Shape)`.

Esa es la única implementación que puede vincularse con seguridad a un código dado sin generar ambigüedad. Por eso, incluso si pasamos un objeto `Rectangle` a `exportShape`, el exportador seguirá llamando al método `export(s: Shape)`.

## Doble despacho

El **doble despacho** es un truco que permite usar el enlace dinámico junto con métodos sobrecargados. Así es como se hace:

class Visitor is
    method visit(s: Shape) is
        print("Visited shape")
    method visit(d: Dot)
        print("Visited dot")

interface Graphic is
    method accept(v: Visitor)

class Shape implements Graphic is
    method accept(v: Visitor)
        // El compilador sabe con certeza que `this` es un `Shape`.
        // Lo que significa que se puede llamar con seguridad a `visit(s: Shape)`.
        v.visit(this)

class Dot extends Shape is
    method accept(v: Visitor)
        // El compilador sabe que `this` es un `Dot`.
        // Lo que significa que se puede llamar con seguridad a `visit(s: Dot)`.
        v.visit(this)

Visitor v = new Visitor();
Graphic g = new Dot();

// El método `accept` está sobrescrito, no sobrecargado. El compilador lo vincula
// dinámicamente. Por lo tanto, `accept` se ejecutará sobre la clase que
// corresponde al objeto que llama al método (en nuestro caso, la clase `Dot`).
g.accept(v);

// Salida: "Visited dot"

### Epílogo

Aunque el patrón [Visitor](/es/design-patterns/visitor) se construye sobre el principio del doble despacho, ese no es su propósito principal. Visitor te permite añadir operaciones «externas» a toda una jerarquía de clases sin cambiar el código existente de esas clases.
