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).