Das Kompositum

Immer wenn sich etwas in einer Baumstruktur darstellen lässt, wird es Zeit über das Kompositum-Muster nachzudenken.

Wenn ich mir zum Beispiel eine Rezeptur anschaue, so besteht die aus Baugruppen (Gewürz, Soße, Panade). Diese Baugruppen bestehen aus Artikeln (Salz, Wasser, Mehl) und/oder weiteren Baugruppen (Curry, Pfeffermischung). Nun soll es möglich sein Operationen auf Teile dieser Rezeptur oder auf das Ganze anzuwenden (Gib mir dein Gewicht, Gib mir deinen Wareneinsatz).


// ---------------------------
//
// Delphi Entwursmustersammlung  -- Composite
//
// Autor: Thomas Groeschke
// Erstellt: 09.08.2008
//
// ---------------------------
unit Composite;

interface

uses
  Messages, Classes, Dialogs, Contnrs;

type

  TComponent = class abstract(TObject)
  private
    Key  : string;
    Name : string;
  public
    function GetName: string; virtual;

    procedure Add(AComponent :TComponent);virtual;
    procedure Remove(AComponent :TComponent);virtual;
    function GetChild(i:integer) : TComponent; virtual;
    function GetCount : integer; virtual;
  end;

  TComposite = class(TComponent)
  private
    FComponent: TObjectList;
  public
    constructor Create(iKey, iName : string);
    function GetName: string; override;

    procedure Add(AComponent :TComponent); override;
    procedure Remove(AComponent :TComponent); override;
    function GetChild(i:integer) : TComponent ; override;
    function GetCount : integer; override;
  end;

  TLeaf = class(TComponent)
  public
    constructor Create(iKey, iName : string);
    function GetName: string; override;
    function GetCount : integer; override;
  end;

implementation

{$REGION 'TComponent'}

procedure TComponent.Remove(AComponent: TComponent);
begin
  ShowMessage('Error');
end;

function TComponent.GetCount: integer;
begin
  result := 0;
end;

function TComponent.GetChild(i: integer): TComponent;
begin
  ShowMessage('Error');
  result := nil;
end;

function TComponent.GetName: string;
begin
  result := Key + ', ' + Name;
end;

procedure TComponent.Add(AComponent: TComponent);
begin
  ShowMessage('Error');
end;

{$ENDREGION}

{$REGION 'TComposite' }

constructor TComposite.Create(iKey, iName: string);
begin
  inherited create;
  Key := iKey;
  Name := iName;
  FComponent := TObjectList.Create;
end;

procedure TComposite.Remove(AComponent: TComponent);
begin
  FComponent.Remove(AComponent);
end;

function TComposite.GetCount: integer;
begin
  result := FComponent.Count;
end;

function TComposite.GetChild(i: integer): TComponent;
begin
  result := TComponent(FComponent.Items[i]);
end;

function TComposite.GetName: string;
begin
  result := 'Kompositum: ' + Key + ', ' + Name;
end;

procedure TComposite.Add(AComponent: TComponent);
begin
  FComponent.Add(AComponent);
end;

{$ENDREGION}

{$REGION 'TLeaf' }

constructor TLeaf.Create(iKey, iName: string);
begin
  inherited create;
  Key := iKey;
  Name := iName;
end;

function TLeaf.GetCount: integer;
begin
  result := 0;
end;

function TLeaf.GetName: string;
begin
  result := 'Blatt: ' + Key + ', ' + Name;
end;

{$ENDREGION}

end.
    Und hier der Quellcode zum Testen:

unit Main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls,
  Composite, Composite2, Iteratoren;

type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    Button1: TButton;
    TreeView2: TTreeView;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    Wurzel : TComponent;
    procedure CreateWurzel;
    procedure FillTree;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  FillTree;
end;

procedure TForm1.CreateWurzel;
var
  E1_Komponente : TComponent;
  E2_Komponente : TComponent;
  E3_Komponente : TComponent;
  E4_Komponente : TComponent;
begin
  Wurzel := TComposite.Create('Wurzel', 'Die Wurzel');
  E1_Komponente := TComposite.Create('E1', '#1 von Ebene 1');
  E2_Komponente := TComposite.Create('E2', '#1 von Ebene 2');
  E3_Komponente := TComposite.Create('E3', '#1 von Ebene 3');
  Wurzel.Add(E1_Komponente);
  E1_Komponente.Add(E2_Komponente);
  E2_Komponente.Add(E3_Komponente);

  E2_Komponente.Add(TLeaf.Create('B1','Blatt1 von E2'));
  E2_Komponente.Add(TLeaf.Create('B2','Blatt2 von E2'));
  E2_Komponente.Add(TLeaf.Create('B3','Blatt3 von E2'));

  E3_Komponente.Add(TLeaf.Create('B1','Blatt1 von E3'));

  E4_Komponente := TComposite.Create('E4', 'E4: Noch jemand da');
  Wurzel.Add(E4_Komponente);
  E4_Komponente.Add(TLeaf.Create('B1','Blatt1 von E4'));
end;

procedure TForm1.FillTree;

  procedure Recur(iActNode: TTreeNode; iKomponente:TComponent);
  var
    ActNode: TTreeNode;
    i : integer;
  begin
    if iKomponente <> nil then
      for i := 0 to iKomponente.GetCount - 1 do
      begin
        ActNode := TreeView1.Items.AddChildObject(iActNode, iKomponente.GetChild(i).GetName, iKomponente.GetChild(i));
        if iKomponente.GetChild(i).GetCount <> 0 then
           Recur(ActNode,iKomponente.GetChild(i));
      end;
  end;

begin
  TreeView1.Items.Clear;
  Recur(nil, Wurzel);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  CreateWurzel;
end;

end.

Ich werde später noch mal auf das Muster zurück kommen und es mit mehr Funktionalität und einem Iterator versehen.

Weiterführende Links:

http://www.klempert.de/nested_sets/

http://www.bluegate.at/tutorials-faqs/design-patterns/nested-sets-verstehen-und-anwenden/

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

*

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>