Design patterns in the test of time: VISITOR

@Ayende ha comenzado una serie de post en lo que revisa algunos patrones de diseño (Go4) luego de transcurridos más de 18 años desde su formalización en el libro Design Patterns: Elements of Reusable Object-Oriented Software.  Inspirado por esa serie, y por el hecho de que en mi última entrevista laboral me preguntaron sobre esto,  voy a presentar una revisión sobre el patrón visitor.

 

Visitor en su forma clásica

La motivación detrás de este patrón es poder añadir funcionalidad a una estructura de datos sin necesidad de modificar las clases que conforman esa estructura. En otras palabras: separar las estructuras de datos de aquellos algoritmos que operan sobre ellas. Si vamos a la Wikipedia vamos a ver este diagrama:

Technorati Tags: ,,

Como se ve, tiene referencias cíclicas, es decir, todos los CarElements deben implementar un método accept que recibe una referencia a un CarElementVisitor y todo CarElementVisitor matiene referencias a todos los tipos que implementan la interface CarElement. Esto es porque C# (el diagrama usa java) no posibilida doble dispatch y así, mediante estas referencias cíclicas es cómo se logra simularla.

Aparte del fuertísimo acoplamiento al que nos fuerza, tiene el inconveniente de ser un patrón que agrega bastante complejidad y que es poco legible debido a su “extraña” sintaxis. Así, si tuviésemos un AST y quisiésemos volverlo código mediante un visitor, nuestro código se vería así:

image

Pero si tratamos de seguir este código, y vamos directamente a la implementación de este método ‘Accept’ nos encontraremos con esto:

image

Y por último, si nos salteamos la abstracción IVisitor y vamos directamente a la implementación del método Visit del PrinterVisitor, nos encontramos con esto otro:

image

Si no conoces el patrón, posiblemente no comprendas este código (y tengamos en cuenta que nos hemos salteado las abstracciones!). Pero el motivo más frecuente por el que no se entiende, si es ese el caso, es porque es complejo.

 

Visitor alternativo

La propuesta que dejo no es mia, ni es nueva. Esta es una implementación muy popular en lenguajes dinámicos.

Partamos del mismo supuesto: tenemos un AST al que queremos transformar en un string. Lo primero que podemos cambiar es la sintaxis confuza del visitor clásico mediante el uso de un Extension Method, como sigue:

image

Ahora veamos cómo se ven los nodos de este árbol:

image

Si bien aquí tenemos una interface INode la cual no luce muy bien, la simplicidad de los nodos es evidente ya que no estamos forzando a implementar ningún método extraño, me refiero al método ‘Accept’. Peo vamos a ver la implementación:

image

Esta implementación es mucho menos compleja y legible, además de reducir notablemente el acoplamiento (los nodos no necesitan conocer al IVisitor ni implementar ningún método extra) lo que posibilita eliminar las referencias cíclicas.

Lucas Ontivero

Sin categoría

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *