El modo Visitante en Delphi amplía el modo Visitante básico. Para obtener más información sobre el modo Visitante, consulte [Gam+, páginas 331..344].
Representa una operación que opera sobre elementos neutralizados de una estructura de objeto. Te permite definir nuevas operaciones que actúan sobre cada elemento sin cambiar su clase.
[Juego+,página 331].
Considere una herramienta de modelado orientada a objetos, como 'Rational Rose, ModelMaker', que representa un modelo como clases y miembros de clase.
La herramienta de modelado proporciona muchas funciones para los miembros operativos, como: enumerar todos los miembros de la clase, generar el marco de código de la clase, ingeniería inversa, etc.
La mayoría de estas operaciones realizan diferentes operaciones en diferentes miembros. Divide a los miembros en campos, métodos,
Propiedades (Propiedades). Por lo tanto, debemos crear clases que manejen campos específicamente, clases que manejen métodos específicamente, etc. Por supuesto, la colección de clases miembro depende del lenguaje que se compila. Pero no hay muchos cambios para un idioma determinado.
La figura muestra el marco de algunas clases de miembros. El problema surge, si distribuyo todas estas operaciones a diferentes clases de miembros,
Hará que todo el sistema sea difícil de entender, modificar y mantener. Combinar la generación de código de clase con la verificación de miembros de la clase crea confusión. Algunas clases deben recompilarse al agregar nuevas operaciones (al menos todos los sistemas relacionados también deben recompilarse). Hay una manera: podría agregar una nueva operación independientemente de la clase miembro como operación que actúa sobre ella.
Para lograr los dos objetivos anteriores, podemos agrupar las operaciones relevantes de cada clase en un objeto independiente (llamado visitante )
Y pase este objeto al miembro actual al iterar por la lista de miembros de la clase. Cuando un miembro "acepta" el acceso, envía una solicitud que contiene su propia información al visitante. El miembro se toma a sí mismo como parámetro. Los visitantes realizan estas acciones.
Por ejemplo: un generador de código que no utiliza visitantes puede generar código a través del método abstracto de la clase miembro: TMember.WriteInterfaceCode(Salida: TStream). Cada miembro llamará a WriteInterfaceCode para generar el código de salida apropiado. Si el código se genera a través de un visitante, se creará un objeto TinterfaceCodeVisitor y se llamará al método AcceptVisitor con el parámetro como objeto de acceso en la lista de miembros. Cada miembro que implemente AcceptVisitor devolverá la llamada al visitante : un campo llamará al método VisitField del visitante y un método llamará al método VisitMethod. De esta manera, la operación WriteInterfaceCode de la clase anterior Tfield ahora se convierte en la operación VisitField de TinterfaceCodeVisitor.
Para que el visitante haga más que solo generar código, necesitamos que todos los visitantes de la lista de miembros tengan una clase principal abstracta TmemberVisitor. TmemberVisitor debe definir un método para cada miembro. Una aplicación que necesita generar miembros en formato HTML definirá una nueva subclase de TmemberVisitor y ya no necesitará agregar código específico de la aplicación a la clase de miembro. El patrón Visitante encapsula cada operación en un Visitante relacionado
Usando el patrón Visitor se deben definir dos niveles de clases: uno correspondiente a los elementos que aceptan operaciones (nivel Tmember) y otro definido para operaciones sobre elementos (nivel TmemberVisitor). Al agregar una nueva operación, simplemente agregue una nueva subclase a la jerarquía de visitantes. Podría simplemente definir una nueva subclase TmemberVisitor para agregar nueva funcionalidad.
El siguiente código demuestra la aplicación del patrón Visitante de clase Tmember descrito anteriormente.
tipo
TMember = clase (TObject)
público
procedimiento AcceptMemberVisitor(Visitor: TMemberVisitor virtual);
fin;
TField = clase (TMembro)
público
procedimiento AcceptMemberVisitor(Visitor: TMemberVisitor anulación);
fin;
TMethod = clase (TMember)
público
procedimiento AcceptMemberVisitor(Visitor: TMemberVisitor anulación);
fin;
TProperty = clase (TMember)
público
procedimiento AcceptMemberVisitor(Visitor: TMemberVisitor anulación);
fin;
TMemberVisitor = clase (TOobjeto)
público
procedimiento VisitField(Instancia: TField virtual);
procedimiento VisitMember (Instancia: TMember virtual);
procedimiento VisitMethod(Instancia: TMethod virtual);
procedimiento VisitaProperty(Instancia: TProperty virtual);
fin;
implementación
{TMembro}
comenzar
Visitante.VisitMember(Self);
fin;
{Campo}
procedimiento TField.AcceptMemberVisitor(Visitor: TMemberVisitor);
comenzar
fin;
{TMétodo}
procedimiento TMethod.AcceptMemberVisitor(Visitor: TMemberVisitor);
comenzar
Visitante.VisitMethod(Self);
fin;
{TPPropiedad}
procedimiento TProperty.AcceptMemberVisitor(Visitante: TMemberVisitor);
comenzar
Visitante.VisitProperty(Self);
fin;
{TMemberVisitor}
procedimiento TMemberVisitor.VisitField (Instancia: TField);
comenzar
fin;
procedimiento TMemberVisitor.VisitMember (Instancia: TMember);
comenzar
fin;
procedimiento TMemberVisitor.VisitMethod(Instancia: TMethod);
comenzar
fin;
procedimiento TMemberVisitor.VisitProperty(Instancia: TProperty);
comenzar
fin;
ilustrar:
? TMember, TField, TMethod y Tproperty implementan el método AcceptMemberVisitor. Estos métodos están integrados en el patrón.
? La clase TMemberVisitor implementa VisitMember, VisitField y otros métodos. TmemberVisitor es una clase abstracta y todos sus métodos se implementan mediante subclases concretas.
A continuación se muestra una implementación de un generador de código simple.
Introducción al código:
• TCodeGenerationVisitor es un visitante del generador de código que implementa el miembro.
? El visitante define una propiedad sensible al contexto: Salida: TTextStream,
? Debe configurarse antes de llamar a VisitXXX. Por ejemplo: DrawingVisitor normalmente requiere un contexto que incluya un lienzo para admitir operaciones de dibujo. El contexto se asigna al generador de código antes de recorrer todo el par de miembros.
? El generador de código consolidará todo el código de la clase generada.
Para comprender realmente el modo Visitante, puede ejecutar este ejemplo y aprender más sobre el mecanismo de doble envío: aceptar/visitar.
unidad CodeGenerators;
interfaz
utiliza Clases, TextStreams;
tipo
TCodeGenerator = clase (TObject)
público
procedimiento Generar (Miembros: TList; Salida: TTextStream);
fin;
implementación
utiliza miembros;
tipo
TCodeGenerationVisitor = clase (TMemberVisitor)
privado
Salida F: TTextStream;
público
procedimiento VisitField(Instancia: TField);
procedimiento VisitMethod(Instancia: TMethod anulación);
procedimiento VisitProperty (Instancia: TProperty);
propiedad Salida: TTextStream leer FOutput escribir FOutput;
fin;
{TCodeGenerationVisitor}
procedimiento TCodeGenerationVisitor.VisitField (Instancia: TField);
comenzar
Output.WriteLnFmt(' %s: %s;', [Instancia.Nombre, Instancia.NombreDeDatos]);
fin;
procedimiento TCodeGenerationVisitor.VisitMethod(Instancia: TMethod);
var
MKStr, DTStr: cadena;
comenzar
caso Instancia.MétodoTipo de
mkConstructor: MKStr := 'constructor';
mkDestructor: MKStr := 'destructor';
mkProcedimiento: MKStr := 'procedimiento';
mkFunción: MKStr := 'función';
fin;
si Instance.MethodKind = mkFunction entonces
DTStr := ': ' + Instancia.NombreDatos
demás
DTStr := ';
{El código está incompleto, es suficiente demostrar la generación del código Tmethod}
Salida.WriteLnFmt(' %s %s%s%s;'
[MKStr, Nombre.Instancia, Parámetros.Instancia, DTStr]);
fin;
procedimiento TCodeGenerationVisitor.VisitProperty(Instancia: TProperty);
comenzar
Output.WriteLnFmt(' propiedad %s: %s lee %s escribe %s;',
[Nombre.Instancia, Nombre.Datos.Instancia,
Instancia.ReadSpecifier, Instancia.WriteSpecifier]);
fin;
{TCodeGenerador}
procedimiento TCodeGenerator.Generate(Miembros: TList; Salida: TTextStream);
var
I: Entero;
comenzar
{Escribir definición de clase}
Output.WriteLine('TSample = clase (TObject)');
{¡bien! Visitantes que se unieron al generador de códigos}
Visitante := TCodeGenerationVisitor.Create;
Intentar
{Recuerde proporcionar contexto para todas las visitas para un mejor acceso a los métodos de VisitXXX. }
para I: = 0 para Members.Count - 1 hacer
{La sección específica del código donde sucedió algo bueno}
TMember(Miembros[I]).AcceptMemberVisitor(Visitante);
finalmente
Visitante.Gratis;
fin;
{Se completó la generación de código para los miembros de la clase}
Salida.WriteLine('fin;');
fin;
organizando
//Muchos extractos de "Patrones de diseño",