AGB  ·  Datenschutz  ·  Impressum  







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

Generisches Visitor-Pattern

Ein Thema von mael · begonnen am 11. Mär 2020 · letzter Beitrag vom 11. Mär 2020
 
Benutzerbild von mael
mael

Registriert seit: 13. Jan 2005
391 Beiträge
 
Delphi XE3 Professional
 
#1

Generisches Visitor-Pattern

  Alt 11. Mär 2020, 10:16
Delphi-Version: 5
Hallo,

Ich versuche ein etwas modifiziertes Visitor-Pattern mit Generics auszustatten.

Hier mal ein minimales Beispiel ohne Generics (bis auf TObjectList<TNode>, aber das ist hier nicht relevant):

Code:
type
  TStringVisitor = class;

  TNode = class
  strict private
    FChildren: TObjectList<TNode>;
    function GetChild(Index: Integer): TNode;
    function GetChildCount: Integer;
  public
    constructor Create;
    destructor Destroy; override;

    procedure Accept(Visitor: TStringVisitor; out Result: string); virtual; abstract;

    procedure AddChild(Node: TNode);

    property ChildCount: Integer read GetChildCount;
    property Children[Index: Integer]: TNode read GetChild;
  end;

  TNodeA = class(TNode)
  strict private
    FValue: Integer;
  public
    constructor Create(Value: Integer);

    procedure Accept(Visitor: TStringVisitor; out Result: string); override;

    property Value: Integer read FValue;
  end;

  TNodeB = class(TNode)
  strict private
    FValue: Double;
  public
    constructor Create(Value: Double);

    procedure Accept(Visitor: TStringVisitor; out Result: string); override;

    property Value: Double read FValue;
  end;

  TStringVisitor = class
  public
    procedure Visit(Node: TNodeA; out Result: string); overload; virtual; abstract;
    procedure Visit(Node: TNodeB; out Result: string); overload; virtual; abstract;
  end;

  TConcatVisitor = class(TStringVisitor)
  public
    procedure Visit(Node: TNodeA; out Result: string); override;
    procedure Visit(Node: TNodeB; out Result: string); override;
  end;



{ TNode }

procedure TNode.AddChild(Node: TNode);
begin
  FChildren.Add(Node);
end;

constructor TNode.Create;
begin
  inherited Create;
  FChildren := TObjectList<TNode>.Create(True);
end;

destructor TNode.Destroy;
begin
  FChildren.Free;
  inherited;
end;

function TNode.GetChild(Index: Integer): TNode;
begin
  Result := FChildren[Index];
end;

function TNode.GetChildCount: Integer;
begin
  Result := FChildren.Count;
end;

{ TNodeA }

procedure TNodeA.Accept(Visitor: TStringVisitor; out Result: string);
begin
  Visitor.Visit(Self, Result);
end;

constructor TNodeA.Create(Value: Integer);
begin
  inherited Create;
  FValue := Value;
end;

{ TNodeB }

procedure TNodeB.Accept(Visitor: TStringVisitor; out Result: string);
begin
  Visitor.Visit(Self, Result);
end;

constructor TNodeB.Create(Value: Double);
begin
  inherited Create;
  FValue := Value;
end;

{ TConcatVisitor }

procedure TConcatVisitor.Visit(Node: TNodeA; out Result: string);
var
  i: Integer;
  ChildRes: string;
begin
  Result := IntToStr(Node.Value);

  for i := 0 to Node.ChildCount - 1 do
  begin
    Node.Children[i].Accept(Self, ChildRes);

    Result := Result + SLineBreak + Childres;
  end;
end;

procedure TConcatVisitor.Visit(Node: TNodeB; out Result: string);
var
  i: Integer;
  ChildRes: string;
begin
  Result := FloatToStr(Node.Value);

  for i := 0 to Node.ChildCount - 1 do
  begin
    Node.Children[i].Accept(Self, ChildRes);

    Result := Result + ', ' + Childres;
  end;
end;

procedure TForm2.FormCreate(Sender: TObject);
var
  t: TNode;
  s: string;
  ConcatVisitor: TConcatVisitor;
begin
  t := TNodeA.Create(10);
  try
    t.AddChild(TNodeB.Create(2.5));

    ConcatVisitor := TConcatVisitor.Create;
    try
      t.Accept(ConcatVisitor, s);
    finally
      ConcatVisitor.Free;
    end;

    ShowMessage(s);
  finally
    t.Free;
  end;
end;
Das funktioniert wie gewünscht.

Aber mal angenommen ich will einen zweiten Visitor machen, der ein Double zurückgibt (die Summe aller Werte der Knoten), müsste ich so etwas verwenden:

Code:
  TSumVisitor = class(TVisitor<Double>)
  public
    procedure Visit(Node: TNodeA; out Result: Double); override;
    procedure Visit(Node: TNodeB; out Result: Double); override;
  end;
Dafür müsste ich allerdings TNode.Accept auch generisch machen, was nicht erlaubt ist weil die Methode auch virtuell ist:

Code:
  TNode = class
  public
    procedure Accept<T>(Visitor: TVisitor<T>; out Result: T); virtual; abstract;
  ...
  end;
Hat jemand eine Idee wie man das lösen kann, so dass man verschiedene Typen zurückgeben kann?
Typecasting eines Pointers würde natürlich gehen, möchte ich aber wegen Typsicherheit vermeiden.

P.S.: Meine Delphi-Version ist XE3, keine Ahnung warum hier 5 steht (nie gehabt).
HxD, schneller Hexeditor:
http://mh-nexus.de/hxd

Geändert von mael (11. Mär 2020 um 10:26 Uhr)
  Mit Zitat antworten Zitat
 


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 16:57 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-2025 by Thomas Breitkreuz