Contexto
He estado usando con una jerarquía de objetos (un árbol de expresión) un patrón de visitante "pseudo" (pseudo, ya que en él no se usa el envío doble):
public interface MyInterface
{
void Accept(SomeClass operationClass);
}
public class MyImpl : MyInterface
{
public void Accept(SomeClass operationClass)
{
operationClass.DoSomething();
operationClass.DoSomethingElse();
// ... and so on ...
}
}
Este diseño fue, sin embargo cuestionable, bastante cómodo ya que la cantidad de implementaciones de MyInterface es significativa (~ 50 o más) y no necesité agregar operaciones adicionales.
Cada implementación es única (es una expresión u operador diferente), y algunos son compuestos (es decir, nodos de operador que contendrán otros nodos de operador / hoja).
El recorrido se realiza actualmente llamando a la operación Aceptar en el nodo raíz del árbol, que a su vez llama Aceptar en cada uno de sus nodos secundarios, que a su vez ... y así sucesivamente ...
Pero ha llegado el momento en que necesito agregar una nueva operación , como una bonita impresión:
public class MyImpl : MyInterface
{
// Property does not come from MyInterface
public string SomeProperty { get; set; }
public void Accept(SomeClass operationClass)
{
operationClass.DoSomething();
operationClass.DoSomethingElse();
// ... and so on ...
}
public void Accept(SomePrettyPrinter printer)
{
printer.PrettyPrint(this.SomeProperty);
}
}
Básicamente veo dos opciones:
- Mantener el mismo diseño, agregando un nuevo método para mi operación a cada clase derivada, a expensas de la capacidad de mantenimiento (no es una opción, en mi humilde opinión)
- Use el patrón de visitante "verdadero", a expensas de la extensibilidad (no es una opción, ya que espero tener más implementaciones en el camino ...), con aproximadamente más de 50 sobrecargas del método de visita, cada una de las cuales coincide con una implementación específica ?
Pregunta
¿Recomendaría usar el patrón de visitante? ¿Hay algún otro patrón que pueda ayudar a resolver este problema?
MyInterface
... ¿todas esas clases tienen una implementación única deDoSomething
yDoSomethingElse
? No veo dónde está su clase visitante atraviesa realmente la jerarquía - se parece más a unafacade
en el momento ..Respuestas:
He estado usando el patrón de visitante para representar árboles de expresión en el transcurso de más de 10 años en seis proyectos a gran escala en tres lenguajes de programación, y estoy muy satisfecho con el resultado. Encontré un par de cosas que facilitaron la aplicación del patrón:
No utilice sobrecargas en la interfaz del visitante.
Ponga el tipo en el nombre del método, es decir, use
más bien que
Agregue un método de "captura desconocida" a su interfaz de visitante.
Permitiría a los usuarios que no pueden modificar su código:
Esto les permitiría construir sus propias implementaciones
IExpression
yIVisitor
eso "comprende" sus expresiones mediante el uso de información de tipo de tiempo de ejecución en la implementación de suVisitExpression
método general.Proporcionar una implementación predeterminada de no hacer nada de la
IVisitor
interfazEsto permitiría a los usuarios que necesitan lidiar con un subconjunto de tipos de expresión construir sus visitantes más rápido y hacer que su código sea inmune a que agreguen más métodos
IVisitor
. Por ejemplo, escribir un visitante que recolecte todos los nombres de variables de sus expresiones se convierte en una tarea fácil, y el código no se romperá incluso si agrega un montón de nuevos tipos de expresiones a suIVisitor
posterior.fuente
Do not use overloads in the interface of the visitor
?