![]() |
Delphi-Version: 2010
Visitor Pattern
Guten morgen,
ich versuche gerade meinen ersten Delphi Visitor zu schreiben und bin natürlich auf die wohl bekannten zirkulären Probleme gestoßen. Ich habe mit der Suche einige Themen gefunden, wo darüber gesprochen wird, eine Lösung aber nur erwähnt wird. Da ich ein recht umfangreiches Composite habe, möchte ich auf keinen Fall alles in eine Datei packen. Ich wär daher daran interessiert ob es überhaupt eine Lösung dazu gibt und wie diese aussieht.
Delphi-Quellcode:
unit Base;
interface type Visitor = class; Base = class public procedure Accept(v : Visitor);virtual;abstract; end; A = class(Base) public procedure Accept(v : Visitor);override; end; B = class(Base) public procedure Accept(v : Visitor);override; end; Visitor = class public procedure Visit(a : A); overload; procedure Visit(b : B); overload; end; implementation { Visitor } procedure Visitor.Visit(a: A); begin WriteLn('A'); end; procedure Visitor.Visit(b: B); begin WriteLn('B'); end; { B } procedure B.Accept(v: Visitor); begin v.Visit(Self); end; { A } procedure A.Accept(v: Visitor); begin v.Visit(Self); end; end.
Delphi-Quellcode:
A soll nun in UnitA, B in UnitB und Visitor in UnitVisitor.
var
list : TList<Base>; v : Visitor; x : Base; begin list := TList<Base>.Create; v := Visitor.Create; list.Add(A.Create); list.Add(B.Create); for x in list do x.Accept(v); list.Free; v.Free; ReadLn; end. Wie löst ihr sowas? Setzt ihr vielleicht auch irgendwelche sinnvollen Alternativen zu Visitor ein? Ich seh bei mir irgendwie nur die Möglichkeit alles in A und B zu implementieren und sie zu Gottklassen mutieren zu lassen. GRüße hansmaad |
AW: Visitor Pattern
Funktioniert nur wenn alle Klassen in der selben Unit deklariert werden.
Aber warum benötigst du eigentlich die Typen A,B? |
AW: Visitor Pattern
Delphi-Quellcode:
Wieso 2 verschiedene Methoden, wo sie doch genau das Selbe machen?
Visitor = class
public procedure Visit(a : A); overload; procedure Visit(b : B); overload; end;
Delphi-Quellcode:
Und schon muß Visitor und Base deine A und B nicht kennen und schon kannst du Visitor und Base in eine Unit packen und A, sowie B lassen sich in anderen Units unterbringen.
Visitor = class
public procedure Visit(b: Base); end; procedure Visitor.Visit(b: Base); begin WriteLn(b.ClassName); end; "x" kann als Basisklasse ja die beiden abgeleiteten Klassen/Objekte entgegennehmen. Es muß sich also in der Hauptunit alles nur mit der Basisklasse erledigen lassen und das Klassenspezifische muß halt überschrieben und von den Subklassen erledigt werden. Aber ansonsten hast du (wie Markus schon sagte) keine Chance und mußt alles in der selben Unit verpacken. |
AW: Visitor Pattern
Zitat:
Das ist doch der Sinn des Vistor Patterns. Für jedes ConcreteElement gibt es unterschiedliches Verhalten, dass aber nicht in der Element Klasse, sondern in einem (konkreten) Visitor implementiert ist. Meine jetzige Alternativlösung ist folgende:
Delphi-Quellcode:
unit UBase;
uses UWriter; type Base = class public function MakeWriter(): Writer; virtual; abstract; end;
Delphi-Quellcode:
unit UWriter;
interface type Writer = class public procedure Write();virtual;abstract; end;
Delphi-Quellcode:
unit UA;
interface uses UBase,UWriter; type A = class(Base) public function MakeWriter() : Writer ;override; end; implementation uses UAWriter; { AA } function A.MakeWriter: Writer; begin Result := AWriter.Create(Self); end;
Delphi-Quellcode:
Das hat gegenüber Visitor den Nachteil, dass ich bei neuer Funktionalität in jeder Elementklasse(A, B...) eine neue Schnittstelle hinzufügen muss, statt nur einen neuen Visitor abzuleiten. Der Nutzen hängt natürlich immer vom Verhältnis Elemente:Vistors ab und was sich wie häufig ändert. Wenn häufig Elemente hinzukommen, kann Visitor auch recht lästig sein.
unit UAWriter;
interface uses UWriter, UA; type AWriter = class(Writer) public constructor Create(x : A);overload; procedure Write();override; end; implementation { AWriter } procedure AWriter.Write; begin WriteLn('A'); end; end. Mir scheint Visitor in Delphi allerdings sehr aufwändig, wenn nicht unmöglich zu implementieren zu sein. Zum konkreten Fall: Base, A, B .. sind Elemente eines Composite (Teile einer Anlage). Stellt euch das WriteLn('A') als Schreiben eines seitenlangen Berichts vor. Von solchen Sachen gibt es ein paar...Dinge, die man möglichst von A, B usw. trennen möchte. |
AW: Visitor Pattern
Zitat:
|
AW: Visitor Pattern
okok... das Beispiel war wohl zu minimiert.
Delphi-Quellcode:
unit Base;
interface type Visitor = class; Base = class public procedure Accept(v : Visitor);virtual;abstract; end; A = class(Base) public procedure Accept(v : Visitor);override; function AZeugs() : String; end; B = class(Base) public procedure Accept(v : Visitor);override; function BZeugs() : String; end; Visitor = class public procedure Visit(a : A); overload; procedure Visit(b : B); overload; end; implementation { Visitor } procedure Visitor.Visit(a: A); begin WriteLn(a.AZeugs()); end; procedure Visitor.Visit(b: B); begin WriteLn(b.BZeugs()); end; //... end. |
AW: Visitor Pattern
Also doch das Gleiche. :gruebel:
Wenn es aufgeteilt werden soll, dann mußt man es auf eine gemeinsame Basis (Base) bringen.
Delphi-Quellcode:
unit BaseUnit;
interface type Visitor = class; Base = class public procedure Accept(v : Visitor); virtual; abstract; function Zeugs() : String; virtual; abstract; end; Visitor = class public procedure Visit(b : Base); end; implementation { Visitor } procedure Visitor.Visit(b: Base); begin WriteLn(b.Zeugs()); end; //... end.
Delphi-Quellcode:
unit AUnit;
interface uses BaseUnit; type A = class(Base) public procedure Accept(v : Visitor); override; function Zeugs() : String; override; end; implementation ...
Delphi-Quellcode:
unit BUnit;
interface uses BaseUnit; type B = class(Base) public procedure Accept(v : Visitor); override; function Zeugs() : String; override; end; implementation ... |
AW: Visitor Pattern
grrr...
Delphi-Quellcode:
Schiff = class(Fahrzeug)
public // ... property Kabinen : Integer read Kabinenanzahl; end
Delphi-Quellcode:
Auto = class(Fahrzeug)
public // ... property Reifen: Integer read Reifenanzahl; end
Delphi-Quellcode:
procedure ReportVisitor.Visit(s : Schiff);
begin WriteLn('Das Schiff hat ' + s.Kabinen + ' Kabinen'); // 100 weitere Schiffsdaten end; procedure ReportVisitor.Visit(a : Auto); begin WriteLn('Das Auto hat ' + s.Reifen+ ' Räder'); // 100 weitere Autodaten end; Nun glaubt mir mal, dass das hier ein potentieller Anwendungsfall des ![]() |
AW: Visitor Pattern
Wenn es aufgeteilt werden soll und sich dann nicht alle Klassen gegenseitig kennen können, dann muß man sich einfach auf eine gemeinsame Basis beschränken, welche sich kennt und wovon dann alles Andere/Unbekannte abgeleitet wird.
Ich denka mal das letze Beispiel dürfte eher dem Pattern entsprechen. Da gibt es dann passende Visitor, die wissen was zu machen ist und welche über einer gemeinsame Basis aufrufbar sind.
Delphi-Quellcode:
unit BaseUnit;
interface type Visitor = class; Base = class public procedure Accept(v : Visitor); virtual; abstract; function Visit(v : Visitor) : Integer; virtual; abstract; end; Visitor = class public procedure Visit(b : Base); end; implementation { Visitor } procedure Visitor.Visit(b : Base); begin WriteLn(b.Visit(self)); end; //... end. //////////////////////////////////// Schiff = class(Base) public procedure Accept(v : Visitor); override; function Visit(v : Visitor) : Integer; override; // ... property Kabinen : Integer read Kabinenanzahl; end; function Schiff.Visit(v : Visitor) : Integer; begin if v is Base then Result := Kabinen else ... end; Wobei man auch auf Seiten des Visitors aufsplitten könnte, aber das wäre dann weniger flexibel.
Delphi-Quellcode:
oder
unit BaseUnit;
interface type Visitor = class; Base = class public procedure Accept(v : Visitor); virtual; abstract; end; StrBase = class(Base) public function StrVisit() : String; virtual; abstract; end; IntBase = class(Base) public function IntVisit() : Integer; virtual; abstract; end; Visitor = class public procedure Visit(b : Base); end; implementation { Visitor } procedure Visitor.Visit(b: Base); begin if b is StrBase then WriteLn(b.StrVisit); else if b is IntBase then WriteLn(b.IntVisit); end; //... end.
Delphi-Quellcode:
unit BaseUnit;
interface type TVisitType = (vtInt, vtStr); Visitor = class; Base = class public procedure Accept(v : Visitor); virtual; abstract; funktion GetVisitType: TVisitType; virtual; abstract; function IntVisit() : Integer; virtual; {abstract;} function StrVisit() : String; virtual; {abstract;} // {...} vielleicht einen leeren Dummy verwenden, // damit man nicht überall alles überschreiben muß end; Visitor = class public procedure Visit(b : Base); end; implementation { Visitor } procedure Visitor.Visit(b: Base); begin case b.GetVisitType of vtInt: WriteLn(b.IntVisit); vtStr: WriteLn(b.StrVisit); else ... end; end; //... end. Eventuell kannst du auch den Visitor aufteilen. Da kennt dann jeder Visitornachfahre seine Base-Class und weiß was mit ihr zu machen ist.
Delphi-Quellcode:
unit BaseUnit;
interface type Visitor = class; Base = class public procedure Accept(v : Visitor); virtual; abstract; procedure Visit(v : Visitor); virtual; abstract; end; Visitor = class public procedure Visit(b : Base); end; implementation { Visitor } procedure Visitor.Visit(b : Base); begin b.Visit(self); end; //... end. /////////////////////////////////////////////////////// unit StrBaseUnit; interface uses BaseUnit; type StrBase = class()Base public procedure Accept(v : Visitor); override; procedure StrVisit; virtual; abstract; end; StrVisitor = class(Visitor) public procedure StrVisit(b : Base); end; implementation { Visitor } procedure StrVisitor.StrVisit(b : Base); begin WriteLn((b as StrBase).StrVisit); end; //... end. |
AW: Visitor Pattern
Also danke für deine Mühen, aber 1. versteh ich nicht alles und 2. hat das denke ich nicht mehr viel mit Visitor zu tun.
Code:
und eine Typunterscheidung in if else, switch case oder Zugriff über die gemeinsame Schnittstelle ist keine Alternative. Dafür brauche ich keinen Visitor.
procedure Visit(b : Base);
Da kann ich auch direkt
Delphi-Quellcode:
oder
for base in Liste do
begin if base is Dies : Tu dies if base is Das : Tu das ... end;
Delphi-Quellcode:
Wenn es mit dem Visitor nicht geht, werde ich eher einen Weg wie in
for base in Liste do
begin TuWasFuerAlleBaseGilt(base); end; ![]() |
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:12 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz