![]() |
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; ![]() |
AW: Visitor Pattern
Keine Ahnung ob dir das hilft:
Delphi-Quellcode:
unit Base;
type TVisistor = class; TBase = class public procedure Accept(v : TVisitor); virtual; abstract; end; TVisitor = class public procedure Visit(ABase: TBase); virtual; abstract; end;
Delphi-Quellcode:
unit ClassA;
uses Base; type TA = class(TBase) public procedure Accept(v : TVisitor); override; end;
Delphi-Quellcode:
unit ClassB;
uses Base; type TB = class(TBase) public procedure Accept(v : TVisitor); override; end;
Delphi-Quellcode:
Statt die Funktionen für die konkreten Klassen im Visitor zu implementieren, könnte man diese auch in spezille Klassen auslagern, die beim Visitor mit der jeweils unterstützten Baseklasse registriert werden.
unit VisitorAB;
uses Base, ClassA, ClassB; type TVisitorAB private procedure VisitA(A: TA); procedure VisitB(B: TB); public procedure Visit(ABase: TBase); override; end; implementation procedure TVisitorAB.Visit(ABase: TBase); begin if ABase is TA then VisitA(TA(ABase)); if ABase is TB then VisitB(TB(ABase)); end; |
AW: Visitor Pattern
Zitat:
Aber der Vorteil mit dem "Visitor" wäre dann, daß diese Unterscheidung dort implementiert ist und man sich extern nicht darum kümmern muß Ich denke mal, das soll wohl der Grund für dieses Pattern sein? :gruebel: Stell dir mal vpr du rufst dieses an meheren Stellen auf, dann müßtest du, bei Einfügen einer neuen Klasse (Base-Nachfahre) an allen Stellen dieses anpassen, anstatt z.B. nur einen passenden Visitor-Nachfahren zu erstellen (falls ein Anderer Visitor es nicht schon kann). |
AW: Visitor Pattern
Zitat:
Zitat:
Wenn allerdings meine Elementklassenhierachie ein fertiges System ist, dass nicht mehr um neue Elemente erweitert wird, aber die Funktionalität dieser Elemente häufig erweitert wird, ist eine Lösung mit Visitor sehr wohl "offen für Erweiterungen". In diesem Fall bleibt nämlich die gesamte Hierachie wie sie ist und man leitet nur einen neuen Visitor von der Visitor Basisklasse ab. |
AW: Visitor Pattern
Wie es scheint, kommt man um hässliche Typecasts nicht herum. Ich hab mir noch eine Lösung geschrieben, die diese Typunterscheidung wenigstens an einer Stelle kapselt und ein normales Visitor 'faked':
Delphi-Quellcode:
unit UBase;
interface type BaseVisitor = class; Base = class public procedure Accept(v : BaseVisitor);virtual;abstract; end; BaseVisitor = class public procedure Visit(b : Base); virtual; abstract; end; implementation end.
Delphi-Quellcode:
unit UnitA;
interface uses UBase; type A = class(Base) Socke_ : Integer; public procedure Accept(v : BaseVisitor);override; property Socke : Integer read Socke_; end; implementation { A } procedure A.Accept(v: BaseVisitor); begin v.Visit(Self); end; end.
Delphi-Quellcode:
unit UnitB;
interface uses UBase; type B = class(Base) private Schnitzel_ :String; public procedure Accept(v : BaseVisitor);override; property Schnitzel : String read Schnitzel_; end; implementation { B } procedure B.Accept(v: BaseVisitor); begin v.Visit(Self); end; end.
Delphi-Quellcode:
unit UVisitor;
interface uses UBase, UConcreteVisitor; type Visitor = class(BaseVisitor) private V_ : ConcreteVisitor; public constructor Create(v : ConcreteVisitor);overload; destructor Destroy();override; procedure Visit(el : Base); override; end; implementation uses SysUtils, UnitA, UnitB; { Visitor } constructor Visitor.Create(v: ConcreteVisitor); begin inherited Create; V_ := v; end; destructor Visitor.Destroy; begin V_.Free; inherited; end; procedure Visitor.Visit(el: Base); begin // Die gekapselte Ekelstelle: if el is A then V_.Visit(A(el)) else if el is B then V_.Visit(B(el)) else Raise Exception.Create('Unknown Element!'); end; end.
Delphi-Quellcode:
unit UConcreteVisitor;
interface uses UnitA,UnitB; type ConcreteVisitor = class public procedure Visit(x : A);overload;virtual;abstract; procedure Visit(x : B);overload;virtual;abstract; end; implementation end.
Delphi-Quellcode:
unit UWriteVisitor;
interface uses UConcreteVisitor, UnitA, UnitB; type WriteVisitor = class(ConcreteVisitor) public procedure Visit(x : A);overload;override; procedure Visit(x : B);overload;override; end; implementation uses SysUtils; { WriterVisitor } procedure WriteVisitor.Visit(x: A); begin WriteLn('Ein A! mit ' + IntToStr(x.Socke)) end; procedure WriteVisitor.Visit(x: B); begin WriteLn('BeeeeH! :>>> ' + x.Schnitzel) end; end.
Delphi-Quellcode:
Neue Visitor erben von ConcreteVisitor und können wie 'normale' Visitor geschrieben werden.
program visit;
{$APPTYPE CONSOLE} uses SysUtils, Generics.Collections, UBase in 'UBase.pas', UVisitor in 'UVisitor.pas', UConcreteVisitor in 'UConcreteVisitor.pas', UnitA in 'UnitA.pas', UnitB in 'UnitB.pas', UWriteVisitor in 'UWriteVisitor.pas'; var list : TList<Base>; v : BaseVisitor; x : Base; begin list := TList<Base>.Create; list.Add(A.Create); list.Add(B.Create); v := Visitor.Create(WriteVisitor.Create); for x in list do x.Accept(v); list.Free; v.Free; ReadLn; end. |
AW: Visitor Pattern
Das ganze läßt sich allerdings viel eleganter, flexibler und nebenbei noch typsicher mit Interfaces realisieren. Dabei können die Basis-Klasse und die abgeleiteten Klassen in unterschiedlichen Units liegen und brauchen über den konkreten Visitor gar nichts wissen. Der Visitor muss natütlich alle Klassen kennen, die er besuchen will.
Delphi-Quellcode:
unit uVisitorTypes;
interface type IVisitor = interface ['{59A6BE7C-6BD7-4746-AA37-F42DCB6D8D01}'] procedure Visit(Instance: IInterface); procedure NotSupported(const Name: string); end; IVisited = interface ['{6C55ECC2-E1AB-43DD-96EC-755FF5C12400}'] procedure Accept(Visitor: IVisitor); end; implementation end.
Delphi-Quellcode:
unit uBase;
interface uses Classes, uVisitorTypes; type { wahlweise auch von TInterfacedObject ableiten } TBase = class(TInterfacedPersistent, IVisited) protected procedure Accept(Visitor: IVisitor); virtual; public end; IBaseVisitor = interface ['{ACD6D82F-8C09-4B13-833B-9C653B99E050}'] procedure VisitBase(Instance: TBase); end; implementation uses SysUtils; procedure TBase.Accept(Visitor: IVisitor); var MyVisitor: IBaseVisitor; begin if Supports(Visitor, IBaseVisitor, MyVisitor) then MyVisitor.VisitBase(Self) else Visitor.NotSupported(ClassName); end; end.
Delphi-Quellcode:
unit unitA;
interface uses uBase, uVisitorTypes; type TA = class(TBase) private FAZeugs: string; protected procedure Accept(Visitor: IVisitor); override; public property AZeugs: string read FAZeugs write FAZeugs; end; IAVisitor = interface ['{3CEF1431-741C-4482-AC9C-1C787DE003C9}'] procedure VisitA(Instance: TA); end; implementation uses SysUtils; procedure TA.Accept(Visitor: IVisitor); var MyVisitor: IAVisitor; begin if Supports(Visitor, IAVisitor, MyVisitor) then MyVisitor.VisitA(Self) else Visitor.NotSupported(ClassName); end; end.
Delphi-Quellcode:
unit unitB;
interface uses uBase, uVisitorTypes; type TB = class(TBase) private FBZeugs: string; protected procedure Accept(Visitor: IVisitor); override; public property BZeugs: string read FBZeugs write FBZeugs; end; IBVisitor = interface ['{801E4A2D-74B6-49FC-8BC6-DCB995AF25F6}'] procedure VisitB(Instance: TB); end; implementation uses SysUtils; procedure TB.Accept(Visitor: IVisitor); var MyVisitor: IBVisitor; begin if Supports(Visitor, IBVisitor, MyVisitor) then MyVisitor.VisitB(Self) else Visitor.NotSupported(ClassName); end; end.
Delphi-Quellcode:
unit uVisitor;
interface uses uVisitorTypes, unitA, unitB; type { wahlweise auch von TInterfacedPersistent ableiten } TVisitor = class(TInterfacedObject, IVisitor, IAVisitor, IBVisitor) protected procedure NotSupported(const Name: string); procedure VisitA(Instance: TA); procedure VisitB(Instance: TB); public procedure Visit(Instance: IInterface); end; implementation uses SysUtils; procedure TVisitor.NotSupported(const Name: string); begin // nach Bedarf ignorieren oder Fehler melden end; procedure TVisitor.Visit(Instance: IInterface); var visited: IVisited; begin if Supports(Instance, IVisited, visited) then visited.Accept(Self) else // nach Bedarf ignorieren oder Fehler melden end; procedure TVisitor.VisitA(Instance: TA); begin Writeln(Instance.AZeugs); end; procedure TVisitor.VisitB(Instance: TB); begin Writeln(Instance.BZeugs); end; end. |
AW: Visitor Pattern
Ok, das sieht ja ganz nett aus (mal davon abgesehen, dass Interfaces für meinen Geschmack generell furchtbar aussehen:wink:).
das IBaseVisitor Interface kann ich mir bei einer abstrakten Basisklasse schenken, da es keine Base Instanzen gibt, korrekt? TVisitor.VisitA(Instance: TA); TVisitor.VisitB(Instance: TB); würde ich wohl abstrakt machen und konkrete Besucher von TVisitor ableiten. Das wäre dann im Stil 'Template Methode'. Dieser Supports / Not Supported Kram wird von der Basisklasse behandelt, der konkrete Besucher kümmert sich nur noch um A und B. Auf den ersten Blick scheint mir das eine ganz brauchbare Lösung zu sein :thumb: Ich werde das mal ausprobieren, Danke! |
AW: Visitor Pattern
Im Normalfall Interface-Parameter besser als const deklarieren.
Sonst erfolgt unnötige Referenzzählung (wie auch bei string).
Delphi-Quellcode:
type
IVisitor = interface ['{59A6BE7C-6BD7-4746-AA37-F42DCB6D8D01}'] procedure Visit(const Instance: IInterface); procedure NotSupported(const Name: string); end; IVisited = interface ['{6C55ECC2-E1AB-43DD-96EC-755FF5C12400}'] procedure Accept(const Visitor: IVisitor); end; |
AW: Visitor Pattern
Zitat:
Delphi-Quellcode:
procedure TA.Accept(Visitor: IVisitor);
var MyVisitor: IAVisitor; begin if Supports(Visitor, IAVisitor, MyVisitor) then MyVisitor.VisitA(Self) else inherited; end;
Delphi-Quellcode:
Wenn nun der Visitor das passende Interface nicht implementiert, wird einfach die Accept-Methode des Vorfahren aufgerufen.
procedure TB.Accept(Visitor: IVisitor);
var MyVisitor: IBVisitor; begin if Supports(Visitor, IBVisitor, MyVisitor) then MyVisitor.VisitB(Self) else inherited; end;
Delphi-Quellcode:
unit uBaseVisitor;
interface uses Classes, uVisitorTypes, uBase; type TBaseVisitor = class(TInterfacedObject, IVisitor, IBaseVisitor) protected procedure NotSupported(const Name: string); procedure VisitBase(Instance: TBase); public procedure Visit(Instance: IInterface); end; implementation uses SysUtils; procedure TBaseVisitor.NotSupported(const Name: string); begin // nach Bedarf ignorieren oder Fehler melden end; procedure TBaseVisitor.Visit(Instance: IInterface); var visited: IVisited; begin if Supports(Instance, IVisited, visited) then visited.Accept(Self) else // nach Bedarf ignorieren oder Fehler melden end; procedure TBaseVisitor.VisitBase(Instance: TBase); begin // tue was mit Instance, was jedes TBase kann end; end. Zitat:
Wenn irgendeine Struktur (Liste, Baum, ...) von TBase-Abkömmligen vorliegt, könnte man einen Visitor schreiben, der die Struktur iteriert und alle Instanzen zählt. Der bräcuhte dann nur IBaseVisitor implementieren. Es gibt da keine allgemeingültige Vorgehensweise - wie so oft in unserer Branche... Ich hatte mal vor, über das Thema "Visitor - eine praktikable Implementierung" einen Artikel zu schreiben, in dem die obige Implentierung erläutert und verschiedene Szenarien durchgespielt werden, aber es kommt halt immer etwas dazwischen. Wär' ja vielleicht auch was für die Delphi-Tage??? |
AW: Visitor Pattern
Zitat:
Was bedeutet es einen const Visitor zu übergeben. Gilt dieses const nur für die Schnittstelle (eigentlich sinnlos da Schnittstellen gar keine Daten haben)? Oder weiß der Compiler, dass die Implementierung von Visit die Visitorinstanz nicht verändert? Was passiert, wenn Visit die Istanz verändert? |
AW: Visitor Pattern
Zitat:
|
AW: Visitor Pattern
Zitat:
Delphi-Quellcode:
: Eine Schreibschutz-Markierung. Das erlaubt natürlich gewisse Compileroptimierungen, genauer gesagt kann einfach ein Pointer auf das Original übergeben werden, statt dass eine Kopie auf dem Stack angelegt werden muss.
const
Zu deiner 2. Frage: Das geht nicht, der Compiler schmeißt dann einen Fehler. Im Grunde ist
Delphi-Quellcode:
das gleiche wie
const
Delphi-Quellcode:
, nur mit dem Unterschied, dass das Original garantiert nicht verändert wird.
var
|
AW: Visitor Pattern
CONST ist nicht immer genau das Gleiche wie VAR,
das siehst du, wenn du z.B.
Delphi-Quellcode:
mit
const i: Integer
Delphi-Quellcode:
und
i: integer
Delphi-Quellcode:
vergleichst, denn dieses wird delphi meißt so weit optimieren, daß hier kein Pointer, sondern direkt der Wert mitgenommen wird, trots des CONST.
var i: Integer
dennoch können, vorallem bei referenzzählenden Typen oder gar dem "kranken" WideString und Records, durch das CONST dennoch so einige kleine Stellen optimiert werden, da man hier explizit angibt, daß der Wert nicht verändert wird. |
AW: Visitor Pattern
Also jetzt brauch ich doch noch mal eine genauere Erklärung.
Ich habe es gerade getestet und ich kann auf einem const Argument alles aufrufen und es auch durch nicht konstante Methoden verändern. Was heißt hier const?
Delphi-Quellcode:
type
Test = class private x : Integer; public constructor Create; procedure Konstant(); procedure NichtKonstant(); end; { Test } constructor Test.Create; begin x := 1; end; procedure Test.Konstant; begin WriteLn(IntToStr(x)); end; procedure Test.NichtKonstant; begin x := x * 2; end; procedure Foo(const t : Test); begin t.Konstant; t.NichtKonstant; t.Konstant; end; var Tester : Test; begin Tester := Test.Create; Foo(Tester); Tester.Free; ReadLn; end.
Code:
1
2 |
AW: Visitor Pattern
CONST bezieht sich immer nur auf die Variable/Parameter.
Es kann also der Parameterwert nicht verändert werden (Interface freigeben oder ein Anderes einfügen), aber den Inhalt von Objekten und Interfaces betrifft dieses nicht.
Delphi-Quellcode:
ändert ja nur den Inhalt und nicht den Parameter, bzw. den Zeiger in dem Parameter, welcher auf das Objekt zeigt.
t.NichtKonstant;
sowas geht nicht
Delphi-Quellcode:
Es gibt nur eine Stelle, wo CONST wirklich versagt ... und das ist bei Records, welche "neuerdings" mit Record-Methoden versehen werden können.
procedure Foo(const t : Test);
begin t := nil; t := WasAnderes; end; Eigentlich dürfte man bei Records, da sie nicht über einen Zeiger, sondern direkt verwalte werden, nicht den Inhalt verändern, welches aber dennoch leider über die RecordMethoden möglich ist. :cry: |
AW: Visitor Pattern
Ah ok, jetzt ists klar.
Gibt es auch ein const, das die Instanz konstant macht. So dass die Funktion das übergebene Objekt nicht verändert darf? |
AW: Visitor Pattern
Leider nein.
Für sowas müßtest du selber den Schreibschutz direkt in das Objekt/Interface einbauen. |
AW: Visitor Pattern
Schade, aber trotzdem Danke:wink:
Gibt es ein empfehlenswertes Buch, dass solche Dinge wie in diesem Thread bespricht (Patterns in Delphi, gutes Design...)? Die meisen Delphi Bücher die ich mir angesehen habe, sahen nach dem Schema "Klicken Sie Form erstellen um ein Form zu erstellen" aus. Gerne auch englisch. |
AW: Visitor Pattern
Nicht Delphi aber gut verständlich und unterhaltsam:
![]() Die Beispiele kann man auch sehr leicht in Delphi umsetzen. |
AW: Visitor Pattern
1) gibt es sowieso fast keine aktuellen Delphibücher. IMHO spricht das FÜR Delphi ;)
und b) Ich denke da sollten Bücher, die andere Sprachen zur Erklärung verwenden auch möglich sein. Sherlock |
AW: Visitor Pattern
Bücher wie GoF usw hab ich schon im Regal stehen. Ich dachte eher an etwas speziell auf Delphi bezogen. Vielleicht verschwenden Delphi Entwickler ja nicht so gern ihre Zeit mit Lesen :lol:
|
AW: Visitor Pattern
|
AW: Visitor Pattern
|
AW: Visitor Pattern
|
AW: Visitor Pattern
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:52 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