Einzelnen Beitrag anzeigen

Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#4

AW: Generisches Visitor-Pattern

  Alt 11. Mär 2020, 16:37
Ich sehe aktuell keinen Grund, das Ganze generisch zu machen. Es ist nur so, daß du das Pattern nicht korrekt umgesetzt hast und deswegen die Probleme mit dem Typ des result-Werts kommst.

So solltest die niemals t.Accept(Visitor) aufrufen, sondern immer Visitor.Visit(t) . Entsprechend muss das Accept gar nichts über den Result wissen, den es ja eh nur an den Visitor weiterreicht.

Eine brauchbare, allerdings so auf die Schnelle hingeschriebene, Implementierung angelehnt an Part 3 der oben genannten Artikelserie wäre sowas:
Delphi-Quellcode:
type
  TVisitor = class(TInterfacedPersistent)
  end;

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

    procedure Accept(Visitor: TVisitor); 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: TVisitor); override;

    property Value: Integer read FValue;
  end;

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

    procedure Accept(Visitor: TVisitor); override;

    property Value: Double read FValue;
  end;

  INodeAVisitor = interface
    ['{290FB9EF-9F7E-4008-9ECA-C52169D200E0}']
    procedure VisitNodeA(Instance: TNodeA);
  end;

  INodeBVisitor = interface
    ['{C893648E-FCA0-4FB2-9400-2AFE3B862256}']
    procedure VisitNodeB(Instance: TNodeB);
  end;

  TNodeVisitor = class(TVisitor, INodeAVisitor, INodeBVisitor)
  protected
    procedure VisitNodeA(Node: TNodeA); virtual; abstract;
    procedure VisitNodeB(Node: TNodeB); virtual; abstract;
  public
    procedure Visit(ANode: TNode); virtual;
  end;

  TConcatVisitor = class(TNodeVisitor)
  private
    FList: TStringList;
    function GetResultValue: string;
  protected
    procedure VisitNodeA(Node: TNodeA); override;
    procedure VisitNodeB(Node: TNodeB); override;
  public
    constructor Create;
    destructor Destroy; override;
    property ResultValue: string read GetResultValue;
  end;
Delphi-Quellcode:
{ 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: TVisitor);
var
  vis: INodeAVisitor;
begin
  if Supports(Visitor, INodeAVisitor, vis) then
    vis.VisitNodeA(Self);
end;

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

{ TNodeB }

procedure TNodeB.Accept(Visitor: TVisitor);
var
  vis: INodeBVisitor;
begin
  if Supports(Visitor, INodeBVisitor, vis) then
    vis.VisitNodeB(Self);
end;

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

procedure TNodeVisitor.Visit(ANode: TNode);
begin
  ANode.Accept(Self);
end;

constructor TConcatVisitor.Create;
begin
  inherited Create;
  FList := TStringList.Create();
end;

destructor TConcatVisitor.Destroy;
begin
  FList.Free;
  inherited Destroy;
end;

function TConcatVisitor.GetResultValue: string;
begin
  Result := FList.Text;
end;

procedure TConcatVisitor.VisitNodeA(Node: TNodeA);
var
  i: Integer;
  res: string;
  saveList: TStringList;
begin
  saveList := FList;
  try
    FList := TStringList.Create;
    try
      FList.Add(IntToStr(Node.Value));
      for i := 0 to Node.ChildCount - 1 do
      begin
        Visit(Node.Children[i]);
      end;
      res := string.Join(sLineBreak, FList.ToStringArray);
    finally
      FList.Free;
    end;
  finally
    FList := saveList;
  end;
  FList.Add(res);
end;

procedure TConcatVisitor.VisitNodeB(Node: TNodeB);
var
  i: Integer;
  res: string;
  saveList: TStringList;
begin
  saveList := FList;
  try
    FList := TStringList.Create;
    try
      FList.Add(FloatToStr(Node.Value));
      for i := 0 to Node.ChildCount - 1 do
      begin
        Visit(Node.Children[i]);
      end;
      res := string.Join(', ', FList.ToStringArray);
    finally
      FList.Free;
    end;
  finally
    FList := saveList;
  end;
  FList.Add(res);
end;
Delphi-Quellcode:
procedure TForm495.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
      ConcatVisitor.Visit(t);
      s := ConcatVisitor.ResultValue;
    finally
      ConcatVisitor.Free;
    end;

    ShowMessage(s);
  finally
    t.Free;
  end;
end;
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat