AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Visitor Pattern

Ein Thema von hansmaad · begonnen am 15. Jul 2010 · letzter Beitrag vom 18. Aug 2010
Antwort Antwort
Seite 1 von 4  1 23     Letzte »    
hansmaad

Registriert seit: 25. Feb 2010
52 Beiträge
 
Delphi 2010 Professional
 
#1

Visitor Pattern

  Alt 15. Jul 2010, 07:48
Delphi-Version: 2010
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:
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.
A soll nun in UnitA, B in UnitB und Visitor in UnitVisitor.
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
  Mit Zitat antworten Zitat
mkinzler
(Moderator)

Registriert seit: 9. Dez 2005
Ort: Heilbronn
39.861 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: Visitor Pattern

  Alt 15. Jul 2010, 07:54
Funktioniert nur wenn alle Klassen in der selben Unit deklariert werden.
Aber warum benötigst du eigentlich die Typen A,B?
Markus Kinzler
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#3

AW: Visitor Pattern

  Alt 15. Jul 2010, 08:01
Delphi-Quellcode:
Visitor = class
public
  procedure Visit(a : A); overload;
  procedure Visit(b : B); overload;
end;
Wieso 2 verschiedene Methoden, wo sie doch genau das Selbe machen?

Delphi-Quellcode:
Visitor = class
public
  procedure Visit(b: Base);
end;

procedure Visitor.Visit(b: Base);
begin
  WriteLn(b.ClassName);
end;
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.

"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.
$2B or not $2B

Geändert von himitsu (15. Jul 2010 um 08:07 Uhr)
  Mit Zitat antworten Zitat
hansmaad

Registriert seit: 25. Feb 2010
52 Beiträge
 
Delphi 2010 Professional
 
#4

AW: Visitor Pattern

  Alt 15. Jul 2010, 08:19
Wieso 2 verschiedene Methoden, wo sie doch genau das Selbe machen?
Sie machen doch nicht das selbe?! Die eine schreibt 'A', die andere schreibt 'B'.
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:
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.
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.

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.
  Mit Zitat antworten Zitat
mkinzler
(Moderator)

Registriert seit: 9. Dez 2005
Ort: Heilbronn
39.861 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: Visitor Pattern

  Alt 15. Jul 2010, 08:22
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.
da sie sich aber im Interface nicht unterscheiden, kannst du der Visitorklasse auch Elemente vom (abstrakten) übergelagerten Typ (Base) übergeben.
Markus Kinzler
  Mit Zitat antworten Zitat
hansmaad

Registriert seit: 25. Feb 2010
52 Beiträge
 
Delphi 2010 Professional
 
#6

AW: Visitor Pattern

  Alt 15. Jul 2010, 08:27
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.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#7

AW: Visitor Pattern

  Alt 15. Jul 2010, 08:53
Also doch das Gleiche.

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
  ...
$2B or not $2B
  Mit Zitat antworten Zitat
hansmaad

Registriert seit: 25. Feb 2010
52 Beiträge
 
Delphi 2010 Professional
 
#8

AW: Visitor Pattern

  Alt 15. Jul 2010, 09:04
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 Visitor Musters ist.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#9

AW: Visitor Pattern

  Alt 15. Jul 2010, 09:26
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:
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.
oder
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.
$2B or not $2B

Geändert von himitsu (15. Jul 2010 um 09:35 Uhr)
  Mit Zitat antworten Zitat
hansmaad

Registriert seit: 25. Feb 2010
52 Beiträge
 
Delphi 2010 Professional
 
#10

AW: Visitor Pattern

  Alt 15. Jul 2010, 09:53
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:
procedure Visit(b : Base);
und eine Typunterscheidung in if else, switch case oder Zugriff über die gemeinsame Schnittstelle ist keine Alternative. Dafür brauche ich keinen Visitor.
Da kann ich auch direkt
Delphi-Quellcode:
for base in Liste do
begin
  if base is Dies : Tu dies
  if base is Das : Tu das
  ...
end;
oder
Delphi-Quellcode:
for base in Liste do
begin
  TuWasFuerAlleBaseGilt(base);
end;
Wenn es mit dem Visitor nicht geht, werde ich eher einen Weg wie in #4 gehen. Ob der Visitor überhaupt das richtige Muster ist müsste ich eh noch entscheiden. Ich wollte nur vorher mal sehen, ob es sich überhautpt lohnt darüber nachzudenken, oder ob es in Delphi einfach quasi unmöglich ist.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 4  1 23     Letzte »    


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 20:44 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz