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/
