O modo Visitante no Delphi estende o modo Visitante básico. Para obter mais informações sobre o modo Visitante, consulte [Gam+, páginas 331..344].
Representa uma operação que opera em elementos neutralizados de uma estrutura de objeto. Permite definir novas operações que atuam sobre cada elemento sem alterar sua classe.
[Gam+,página 331].
Considere uma ferramenta de modelagem orientada a objetos, como 'Rational Rose, ModelMaker', que representa um modelo como classes e membros de classe.
A ferramenta de modelagem fornece diversas funções para membros operacionais, como: listar todos os membros da classe, gerar a estrutura de código da classe, fazer engenharia reversa, etc.
A maioria dessas operações executa operações diferentes em membros diferentes. Ele divide os membros em campos, métodos,
Propriedades (PRoperties). Portanto, devemos criar classes que manipulem especificamente campos, classes que manipulem especificamente métodos e assim por diante. A coleção de classes membros depende, obviamente, da linguagem que está sendo compilada. Mas não muda muita coisa para um determinado idioma.
A figura mostra a estrutura de algumas classes membros. O problema surge, se eu espalhar todas essas operações para diferentes classes de membros,
Isso tornará todo o sistema difícil de entender, modificar e manter. Colocar a geração de código de classe junto com a verificação dos membros da classe cria confusão. Algumas classes precisam ser recompiladas ao adicionar novas operações (pelo menos todos os sistemas relacionados também devem ser recompilados). Existe uma maneira: você pode adicionar uma nova operação independentemente da classe membro como a operação que atua nela.
Para atingir os dois objetivos acima, podemos agrupar as operações relevantes em cada classe em um objeto independente (chamado visitante )
E passe esse objeto para o membro atual ao iterar pela lista de membros da classe. Quando um membro 'aceita' o acesso, o membro envia uma solicitação contendo suas próprias informações ao visitante. O membro se toma como parâmetro. Os visitantes realizam essas ações.
Por exemplo: um gerador de código que não utiliza visitantes pode gerar código através do método abstrato da classe membro: TMember.WriteInterfaceCode(Output: TStream). Cada membro chamará WriteInterfaceCode para gerar o código de saída apropriado. Caso o código seja gerado através de um visitante, será criado um objeto TinterfaceCodeVisitor e o método AcceptVisitor cujo parâmetro é o objeto de acesso será chamado na lista de membros. Cada membro que implementa AcceptVisitor chamará de volta o visitante : um campo chamará o método VisitField do visitante e um método chamará o método VisitMethod. Desta forma, a operação WriteInterfaceCode da classe anterior Tfield agora se torna a operação VisitField de TinterfaceCodeVisitor.
Para fazer com que o visitante faça mais do que apenas gerar código, precisamos que todos os visitantes da lista de membros tenham uma classe pai abstrata TmemberVisitor. TmemberVisitor deve definir um método para cada membro. Um aplicativo que precisa enviar membros para o formato HTML definirá uma nova subclasse de TmemberVisitor e não precisará mais adicionar código específico do aplicativo à classe membro. O padrão Visitor encapsula cada operação em um Visitor relacionado
Utilizando o padrão Visitor devem ser definidos dois níveis de classes: um correspondente aos elementos que aceitam operações (nível Tmember) e outro definido para operações sobre elementos (nível TmemberVisitor). Ao adicionar uma nova operação, basta adicionar uma nova subclasse à hierarquia de visitantes. Eu poderia simplesmente definir uma nova subclasse TmemberVisitor para adicionar novas funcionalidades.
O código a seguir demonstra a aplicação do padrão Visitor da classe Tmember descrito acima.
tipo
TMember = classe (TObject)
público
procedimento AcceptMemberVisitor(Visitor: TMemberVisitor virtual);
fim;
TField = classe (TMember)
público
procedimento AcceptMemberVisitor(Visitor: substituição de TMemberVisitor);
fim;
TMethod = classe (TMember)
público
procedimento AcceptMemberVisitor(Visitor: substituição de TMemberVisitor);
fim;
TProperty = classe (TMember)
público
procedimento AcceptMemberVisitor(Visitor: substituição de TMemberVisitor);
fim;
TMemberVisitor = classe (TObject)
público
procedimento VisitField(Instância: TField);
procedimento VisitMember(Instância: TMember virtual);
procedimento VisitMethod(Instância: TMethod virtual);
procedimento VisitProperty(Instância: TProperty virtual);
fim;
implementação
{TMembro}
começar
Visitante.VisitMember(Self);
fim;
{TFcampo}
procedimento TField.AcceptMemberVisitor(Visitor: TMemberVisitor);
começar
fim;
{TMétodo}
procedimento TMethod.AcceptMemberVisitor(Visitor: TMemberVisitor);
começar
Visitor.VisitMethod(Self);
fim;
{TPPropriedade}
procedimento TProperty.AcceptMemberVisitor(Visitor: TMemberVisitor);
começar
Visitante.VisitProperty(Self);
fim;
{ TMemberVisitor }
procedimento TMemberVisitor.VisitField(Instância: TField);
começar
fim;
procedimento TMemberVisitor.VisitMember(Instância: TMember);
começar
fim;
procedimento TMemberVisitor.VisitMethod(Instância: TMethod);
começar
fim;
procedimento TMemberVisitor.VisitProperty(Instância: TProperty);
começar
fim;
ilustrar:
? TMember, TField, TMethod e Tproperty implementam o método AcceptMemberVisitor. Esses métodos estão incorporados no padrão.
? A classe TMemberVisitor implementa VisitMember, VisitField e outros métodos. TmemberVisitor é uma classe abstrata e todos os seus métodos são implementados por subclasses concretas.
Abaixo está uma implementação de um gerador de código simples.
Introdução do código:
• TCodeGenerationVisitor é um visitante do gerador de código que implementa o membro.
? O visitante define uma propriedade sensível ao contexto: Saída: TTextStream,
? Deve ser definido antes de VisitXXX ser chamado. Por exemplo: DrawingVisitor normalmente requer um contexto incluindo tela para suportar operações de desenho. O contexto é atribuído ao gerador de código antes da iteração por todo o par de membros.
? O gerador de código consolidará todo o código da classe gerada
Para realmente entender o modo Visitante, você pode executar este exemplo e aprender mais sobre o mecanismo de duplo envio: aceitar/visitar.
geradores de códigos unitários;
interface
usa Classes, TextStreams;
tipo
TCodeGenerator = classe (TObject)
público
procedimento Generate(Membros: TList; Saída: TTextStream);
fim;
implementação
usa membros;
tipo
TCodeGenerationVisitor = classe (TMemberVisitor)
privado
FSaída: TTextStream;
público
procedimento VisitField(Instância: TField);
procedimento VisitMethod(Instância: substituição de TMethod);
procedimento VisitProperty(Instância: TProperty override);
propriedade Saída: TTextStream lê FOutput escreve FOutput;
fim;
{TCodeGenerationVisitor}
procedimento TCodeGenerationVisitor.VisitField(Instância: TField);
começar
Output.WriteLnFmt(' %s: %s;', [Instance.Name, Instance.DataName]);
fim;
procedimento TCodeGenerationVisitor.VisitMethod(Instância: TMethod);
var
MKStr, DTStr: string;
começar
caso Instance.MethodKind de
mkConstrutor: MKStr := 'construtor';
mkDestructor: MKStr := 'destruidor';
mkProcedure: MKStr := 'procedimento';
mkFuntion: MKStr := 'função';
fim;
se Instance.MethodKind = mkFunction então
DTStr := ': ' + Instance.DataName
outro
DTStr := ';
{O código está incompleto, é suficiente demonstrar a geração de código do método T}
Saída.WriteLnFmt(' %s %s%s%s;'
[MKStr, Instance.Name, Instance.Parameters, DTStr]);
fim;
procedimento TCodeGenerationVisitor.VisitProperty(Instância: TProperty);
começar
Output.WriteLnFmt(' propriedade %s: %s leitura %s gravação %s;',
[Instância.Nome, Instância.NomeDados,
Instance.ReadSpecifier, Instance.WriteSpecifier]);
fim;
{TCodeGenerator}
procedimento TCodeGenerator.Generate(Membros: TList; Saída: TTextStream);
var
Eu: Inteiro;
começar
{Escrever definição de classe}
Output.WriteLine('TSample = classe (TObject)');
{bom! Visitantes que aderiram ao gerador de código}
Visitante := TCodeGenerationVisitor.Create;
Tentar
{Lembre-se de fornecer contexto para todas as visitas para melhor acesso aos métodos VisitXXX. }
para I := 0 para Members.Count - 1 do
{A seção específica do código onde algo bom aconteceu}
TMember(Membros[I]).AcceptMemberVisitor(Visitor);
finalmente
Visitante.Grátis;
fim;
{Geração de código para alunos concluída}
Saída.WriteLine('fim;');
fim;
Organizando
//Muitos trechos de "Design Patterns",