Le mode Visiteur dans Delphi étend le mode Visiteur de base. Pour plus d'informations sur le mode Visiteur, veuillez vous référer à [Gam+, pages331..344].
Représente une opération qui opère sur les éléments neutralisés d’une structure d’objet. Il permet de définir de nouvelles opérations qui agissent sur chaque élément sans changer sa classe.
[Gam+,page331].
Considérons un outil de modélisation orienté objet, tel que « Rational Rose, ModelMaker », qui représente un modèle sous forme de classes et de membres de classe.
L'outil de modélisation fournit de nombreuses fonctions pour les membres opérationnels, telles que : lister tous les membres de la classe, générer le framework de code de la classe, la rétro-ingénierie, etc.
La plupart de ces opérations effectuent différentes opérations sur différents membres. Il divise les membres en champs, méthodes,
Propriétés (PRerties). Par conséquent, nous devons créer des classes qui gèrent spécifiquement les champs, des classes qui gèrent spécifiquement les méthodes, etc. La collection de classes membres dépend bien sûr de la langue compilée. Mais peu de changements pour une langue donnée.
La figure montre le cadre de certaines classes membres. Le problème se pose, si je répartis toutes ces opérations sur différentes classes membres,
Cela rendra l’ensemble du système difficile à comprendre, à modifier et à maintenir. Associer la génération de code de classe à la vérification des membres de la classe crée de la confusion. Certaines classes doivent être recompilées lors de l'ajout de nouvelles opérations (au moins tous les systèmes associés doivent également être recompilés). Il existe un moyen : vous pouvez ajouter une nouvelle opération indépendamment de la classe membre en tant qu'opération qui agit sur elle.
Pour atteindre les deux objectifs ci-dessus, nous pouvons envelopper les opérations pertinentes dans chaque classe dans un objet indépendant (appelé visiteur )
Et transmettez cet objet au membre actuel lors de la itération dans la liste des membres de la classe. Lorsqu'un membre « accepte » l'accès, il envoie une demande contenant ses propres informations au visiteur. Le membre se prend en paramètre. Les visiteurs effectuent ces actions.
Par exemple : un générateur de code qui n'utilise pas de visiteurs peut générer du code via la méthode abstraite de la classe membre : TMember.WriteInterfaceCode(Output : TStream). Chaque membre appellera WriteInterfaceCode pour générer le code de sortie approprié. Si le code est généré via un visiteur, un objet TinterfaceCodeVisitor sera créé et la méthode AcceptVisitor avec le paramètre étant l'objet d'accès sera appelée sur la liste des membres. Chaque membre implémentant AcceptVisitor rappellera le visiteur : un champ appellera la méthode VisitField du visiteur, et une méthode appellera la méthode VisitMethod. De cette façon, l'opération WriteInterfaceCode de la classe précédente Tfield devient désormais l'opération VisitField de TinterfaceCodeVisitor.
Pour que le visiteur fasse plus que simplement générer du code, nous avons besoin que tous les visiteurs de la liste de membres aient une classe parent abstraite TmemberVisitor. TmemberVisitor doit définir une méthode pour chaque membre. Une application qui doit afficher les membres au format HTML définira une nouvelle sous-classe de TmemberVisitor et n'aura plus besoin d'ajouter du code spécifique à l'application à la classe membre. Le modèle Visiteur encapsule chaque opération dans un Visiteur associé
A l'aide du pattern Visitor, deux niveaux de classes doivent être définis : l'un correspondant aux éléments acceptant les opérations (niveau Tmember) et l'autre défini pour les opérations sur les éléments (niveau TmemberVisitor). Lors de l'ajout d'une nouvelle opération, ajoutez simplement une nouvelle sous-classe à la hiérarchie des visiteurs. Je pourrais simplement définir une nouvelle sous-classe TmemberVisitor pour ajouter de nouvelles fonctionnalités.
Le code suivant illustre l’application du modèle Visiteur de classe Tmember décrit ci-dessus.
taper
TMember = classe (TObject)
publique
procédure AcceptMemberVisitor(Visitor: TMemberVisitor);
fin;
TField = classe (TMember)
publique
procédure AcceptMemberVisitor (Visiteur : remplacement de TMemberVisitor );
fin;
TMethod = classe (TMember)
publique
procédure AcceptMemberVisitor (Visiteur : remplacement de TMemberVisitor );
fin;
TProperty = classe (TMember)
publique
procédure AcceptMemberVisitor (Visiteur : remplacement de TMemberVisitor );
fin;
TMemberVisitor = classe (TObject)
publique
procédure VisitField(Instance : TField); virtuel ;
procédure VisitMember (Instance : TMember) ;
procédure VisitMethod (Instance : TMethod) ;
procédure VisitProperty(Instance : TProperty); virtuel ;
fin;
mise en œuvre
{TMembre}
commencer
Visiteur.VisitMember(Self);
fin;
{Champ}
procédure TField.AcceptMemberVisitor(Visiteur : TMemberVisitor);
commencer
fin;
{ TMéthode }
procédure TMethod.AcceptMemberVisitor(Visiteur : TMemberVisitor);
commencer
Visiteur.VisitMethod(Self);
fin;
{PropriétéTP}
procédure TProperty.AcceptMemberVisitor(Visiteur : TMemberVisitor);
commencer
Visiteur.VisitProperty(Self);
fin;
{ TMembreVisiteur }
procédure TMemberVisitor.VisitField(Instance : TField);
commencer
fin;
procédure TMemberVisitor.VisitMember(Instance : TMember);
commencer
fin;
procédure TMemberVisitor.VisitMethod(Instance : TMethod);
commencer
fin;
procédure TMemberVisitor.VisitProperty(Instance : TProperty);
commencer
fin;
illustrer:
? TMember, TField, TMethod et Tproperty implémentent tous la méthode AcceptMemberVisitor. Ces méthodes sont intégrées au modèle.
? La classe TMemberVisitor implémente VisitMember, VisitField et d'autres méthodes. TmemberVisitor est une classe abstraite et toutes ses méthodes sont implémentées par des sous-classes concrètes.
Vous trouverez ci-dessous une implémentation d'un générateur de code simple.
Présentation du code :
• TCodeGenerationVisitor est un visiteur pour le générateur de code qui implémente le membre.
? Le visiteur définit une propriété contextuelle : Sortie : TTextStream,
? Il doit être défini avant l'appel de VisitXXX. Par exemple : DrawingVisitor nécessite généralement un contexte incluant un canevas pour prendre en charge les opérations de dessin. Le contexte est attribué au générateur de code avant de parcourir toute la paire de membres.
? Le générateur de code consolidera tout le code de la classe générée
Pour bien comprendre le mode Visiteur, vous pouvez exécuter cet exemple et apprendre davantage le mécanisme de double répartition : accepter/visiter.
Générateurs de codes d'unité ;
interface
utilise des classes, TextStreams ;
taper
TCodeGenerator = classe (TObject)
publique
procédure Generate (Membres : TList ; Sortie : TTextStream);
fin;
mise en œuvre
utilise des membres ;
taper
TCodeGenerationVisitor = classe (TMemberVisitor)
privé
FOutput : TTextStream ;
publique
procédure VisitField (Instance : TField );
procédure VisitMethod (Instance : TMethod);
procédure VisitProperty (Instance : TProperty );
propriété Sortie : TTextStream lit FOutput écrit FOutput ;
fin;
{TCodeGenerationVisitor}
procédure TCodeGenerationVisitor.VisitField(Instance : TField);
commencer
Output.WriteLnFmt(' %s: %s;', [Instance.Name, Instance.DataName]);
fin;
procédure TCodeGenerationVisitor.VisitMethod (Instance : TMethod);
var
MKStr, DTStr : chaîne ;
commencer
case Instance.MethodKind of
mkConstructor : MKStr := 'constructeur';
mkDestructor : MKStr := 'destructeur';
mkProcédure : MKStr := 'procédure';
mkFuntion : MKStr := 'fonction' ;
fin;
si Instance.MethodKind = mkFunction alors
DTStr := ': ' + Instance.DataName
autre
DTStr := ';
{Le code est incomplet, il suffit de démontrer la génération de code par la méthode T}
Output.WriteLnFmt(' %s %s%s%s;'
[MKStr, Instance.Name, Instance.Parameters, DTStr]);
fin;
procédure TCodeGenerationVisitor.VisitProperty(Instance : TProperty);
commencer
Output.WriteLnFmt(' propriété %s : %s lecture %s écriture %s;',
[Instance.Nom, Instance.DataName,
Instance.ReadSpecifier, Instance.WriteSpecifier]);
fin;
{TCodeGenerator}
procédure TCodeGenerator.Generate(Membres : TList ; Sortie : TTextStream);
var
I : Entier ;
commencer
{Écrire la définition de la classe}
Output.WriteLine('TSample = classe (TObject)');
{bien! Visiteurs qui ont rejoint le générateur de code}
Visiteur := TCodeGenerationVisitor.Create;
Essayer
{N'oubliez pas de fournir un contexte pour toutes les visites pour un meilleur accès aux méthodes VisitXXX. }
pour I := 0 à Members.Count - 1 faire
{La section spécifique du code où quelque chose de bien s'est produit}
TMember(Membres[I]).AcceptMemberVisitor(Visiteur);
enfin
Visiteur.Gratuit ;
fin;
{Génération de code pour les membres de la classe terminée}
Output.WriteLine('end;');
fin;
Organisation
//De nombreux extraits de "Design Patterns",