Der Besuchermodus in Delphi erweitert den grundlegenden Besuchermodus. Weitere Informationen zum Besuchermodus finden Sie unter [Gam+, Seiten 331..344].
Stellt einen Vorgang dar, der neutralisierte Elemente einer Objektstruktur bearbeitet. Sie können damit neue Operationen definieren, die auf jedes Element wirken, ohne seine Klasse zu ändern.
[Gam+,Seite331].
Stellen Sie sich ein objektorientiertes Modellierungstool wie „Rational Rose, ModelMaker“ vor, das ein Modell als Klassen und Klassenmitglieder darstellt.
Das Modellierungstool bietet viele Funktionen für den Betrieb von Mitgliedern, z. B. Auflisten aller Mitglieder der Klasse, Generieren des Code-Frameworks der Klasse, Reverse Engineering usw.
Die meisten dieser Vorgänge führen unterschiedliche Vorgänge für verschiedene Mitglieder aus. Es unterteilt Mitglieder in Felder, Methoden,
Eigenschaften (Eigenschaften). Daher müssen wir Klassen erstellen, die speziell Felder verarbeiten, Klassen, die speziell Methoden verarbeiten usw. Die Sammlung von Mitgliedsklassen hängt natürlich von der zu kompilierenden Sprache ab. Für eine bestimmte Sprache ändert sich jedoch nicht viel.
Die Abbildung zeigt den Rahmen einiger Mitgliedsklassen. Das Problem entsteht, wenn ich alle diese Operationen auf verschiedene Mitgliedsklassen verteile.
Dadurch wird es schwierig, das gesamte System zu verstehen, zu ändern und zu warten. Die Kombination der Klassencodegenerierung mit der Überprüfung von Klassenmitgliedern führt zu Verwirrung. Einige Klassen müssen beim Hinzufügen neuer Operationen neu kompiliert werden (zumindest alle zugehörigen Systeme müssen ebenfalls neu kompiliert werden). Es gibt einen Weg: Sie könnten eine neue Operation unabhängig von der Mitgliedsklasse als die Operation hinzufügen, die auf sie einwirkt.
Um die beiden oben genannten Ziele zu erreichen, können wir die relevanten Operationen in jeder Klasse in ein unabhängiges Objekt ( Besucher genannt) einschließen.
Und übergeben Sie dieses Objekt an das aktuelle Mitglied, wenn Sie die Klassenmitgliederliste durchlaufen. Wenn ein Mitglied den Zugriff „akzeptiert“, sendet das Mitglied eine Anfrage mit seinen eigenen Informationen an den Besucher. Das Mitglied nimmt sich selbst als Parameter. Besucher führen diese Aktionen aus.
Beispiel: Ein Codegenerator, der keine Besucher verwendet, kann Code über die abstrakte Methode der Mitgliedsklasse generieren: TMember.WriteInterfaceCode(Ausgabe: TStream). Jedes Mitglied ruft WriteInterfaceCode auf, um den entsprechenden Ausgabecode zu generieren. Wenn der Code durch einen Besucher generiert wird, wird ein TinterfaceCodeVisitor-Objekt erstellt und die AcceptVisitor-Methode mit dem Parameter, der das Zugriffsobjekt ist, wird für die Mitgliederliste aufgerufen. Jedes Mitglied, das AcceptVisitor implementiert, ruft den Besucher zurück : Ein Feld ruft die VisitField-Methode des Besuchers auf und eine Methode ruft die VisitMethod-Methode auf. Auf diese Weise wird die WriteInterfaceCode-Operation der vorherigen Klasse Tfield nun zur VisitField-Operation von TinterfaceCodeVisitor.
Damit der Besucher mehr tun kann als nur Code zu generieren, müssen alle Mitgliederlistenbesucher über eine abstrakte übergeordnete Klasse TmemberVisitor verfügen. TmemberVisitor muss für jedes Mitglied eine Methode definieren. Eine Anwendung, die Mitglieder im HTML-Format ausgeben muss, definiert eine neue Unterklasse von TmemberVisitor und muss der Mitgliedsklasse keinen anwendungsspezifischen Code mehr hinzufügen. Das Besuchermuster kapselt jede Operation in einem zugehörigen Besucher
Mithilfe des Besuchermusters müssen zwei Klassenebenen definiert werden: eine für die Elemente, die Operationen akzeptieren (Tmember-Ebene), und die andere für Operationen an Elementen (TmemberVisitor-Ebene). Wenn Sie einen neuen Vorgang hinzufügen, fügen Sie einfach eine neue Unterklasse zur Besucherhierarchie hinzu. Ich könnte einfach eine neue TmemberVisitor-Unterklasse definieren, um neue Funktionalität hinzuzufügen.
Der folgende Code demonstriert die Anwendung des oben beschriebenen Besuchermusters der Klasse Tmember.
Typ
TMember = Klasse (TObject)
öffentlich
procedure AcceptMemberVisitor(Visitor: TMemberVisitor);
Ende;
TField = Klasse (TMember)
öffentlich
procedure AcceptMemberVisitor(Visitor: TMemberVisitor); override;
Ende;
TMethod = Klasse (TMember)
öffentlich
procedure AcceptMemberVisitor(Visitor: TMemberVisitor); override;
Ende;
TProperty = Klasse (TMember)
öffentlich
procedure AcceptMemberVisitor(Visitor: TMemberVisitor); override;
Ende;
TMemberVisitor = Klasse (TObject)
öffentlich
procedure VisitField(Instance: TField);
procedure VisitMember(Instance: TMember virtual);
procedure VisitMethod(Instance: TMethod virtual);
procedure VisitProperty(Instance: TProperty virtual);
Ende;
Durchführung
{TMember}
beginnen
Visitor.VisitMember(Selbst);
Ende;
{TField}
procedure TField.AcceptMemberVisitor(Visitor: TMemberVisitor);
beginnen
Ende;
{ TMethod }
procedure TMethod.AcceptMemberVisitor(Visitor: TMemberVisitor);
beginnen
Visitor.VisitMethod(Self);
Ende;
{TPProperty}
procedure TProperty.AcceptMemberVisitor(Visitor: TMemberVisitor);
beginnen
Visitor.VisitProperty(Self);
Ende;
{ TMemberVisitor }
procedure TMemberVisitor.VisitField(Instance: TField);
beginnen
Ende;
procedure TMemberVisitor.VisitMember(Instance: TMember);
beginnen
Ende;
procedure TMemberVisitor.VisitMethod(Instance: TMethod);
beginnen
Ende;
procedure TMemberVisitor.VisitProperty(Instance: TProperty);
beginnen
Ende;
veranschaulichen:
? TMember, TField, TMethod und Tproperty implementieren alle die AcceptMemberVisitor-Methode. Diese Methoden sind in das Muster eingebettet
? Die TMemberVisitor-Klasse implementiert VisitMember, VisitField und andere Methoden. TmemberVisitor ist eine abstrakte Klasse und alle ihre Methoden werden von konkreten Unterklassen implementiert.
Nachfolgend finden Sie eine Implementierung eines einfachen Codegenerators.
Code-Einführung:
• TCodeGenerationVisitor ist ein Besucher für den Codegenerator, der das Mitglied implementiert.
? Der Besucher definiert eine kontextsensitive Eigenschaft: Ausgabe: TTextStream,
? Es muss festgelegt werden, bevor VisitXXX aufgerufen wird. Beispielsweise erfordert DrawingVisitor normalerweise einen Kontext einschließlich Canvas, um Zeichenvorgänge zu unterstützen. Der Kontext wird dem Codegenerator zugewiesen, bevor das gesamte Mitgliedspaar durchlaufen wird.
? Der Codegenerator konsolidiert den gesamten Code für die generierte Klasse
Um den Besuchermodus wirklich zu verstehen, können Sie dieses Beispiel ausführen und den doppelten Versandmechanismus weiter erlernen: Akzeptieren/Besuchen.
Einheit CodeGeneratoren;
Schnittstelle
verwendet Klassen, TextStreams;
Typ
TCodeGenerator = Klasse (TObject)
öffentlich
procedure Generate(Members: TList; Output: TTextStream);
Ende;
Durchführung
verwendet Mitglieder;
Typ
TCodeGenerationVisitor = Klasse (TMemberVisitor)
Privat
FOutput: TTextStream;
öffentlich
procedure VisitField(Instance: TField);
procedure VisitMethod(Instance: TMethod);
procedure VisitProperty(Instance: TProperty);
Eigenschaft Ausgabe: TTextStream read FOutput write FOutput;
Ende;
{TCodeGenerationVisitor}
procedure TCodeGenerationVisitor.VisitField(Instance: TField);
beginnen
Output.WriteLnFmt(' %s: %s;', [Instance.Name, Instance.DataName]);
Ende;
procedure TCodeGenerationVisitor.VisitMethod(Instance: TMethod);
var
MKStr, DTStr: string;
beginnen
case Instance.MethodKind of
mkConstructor: MKStr := 'constructor';
mkDestructor: MKStr := 'destructor';
mkProcedure: MKStr := 'Prozedur';
mkFuntion: MKStr := 'function';
Ende;
wenn Instance.MethodKind = mkFunction dann
DTStr := ': ' + Instance.DataName
anders
DTStr := ';
{Der Code ist unvollständig, er reicht aus, um die Tmethod-Codegenerierung zu demonstrieren}
Output.WriteLnFmt(' %s %s%s%s;'
[MKStr, Instance.Name, Instance.Parameters, DTStr]);
Ende;
procedure TCodeGenerationVisitor.VisitProperty(Instance: TProperty);
beginnen
Output.WriteLnFmt(' Eigenschaft %s: %s lesen %s schreiben %s;',
[Instanzname, Instanzname.Datenname,
Instance.ReadSpecifier, Instance.WriteSpecifier]);
Ende;
{TCodeGenerator}
procedure TCodeGenerator.Generate(Members: TList; Output: TTextStream);
var
I: Ganzzahl;
beginnen
{Klassendefinition schreiben}
Output.WriteLine('TSample = class (TObject)');
{Gut! Besucher, die dem Codegenerator beigetreten sind}
Besucher := TCodeGenerationVisitor.Create;
Versuchen
{Denken Sie daran, für alle Besuche Kontext bereitzustellen, um einen besseren Zugriff auf die VisitXXX-Methoden zu ermöglichen. }
for I := 0 to Members.Count - 1 do
{Der spezifische Codeabschnitt, in dem etwas Gutes passiert ist}
TMember(Members[I]).AcceptMemberVisitor(Visitor);
Endlich
Besucherfrei;
Ende;
{Codegenerierung für Klassenmitglieder abgeschlossen}
Output.WriteLine('end;');
Ende;
Organisieren
//Viele Auszüge aus „Design Patterns“,