Composite Design Pattern in Delphi
The Composite Design Pattern lets clients treat individual objects and compositions of objects uniformly
The idea is to define a Composite Class
AComponentClass = class
//...
procedure DoSomething; virtual;
end;
ACompositeClass = class(aComponentClass)
aList: tList; // List of "aComponentClass" Objects
procedure DoSomething; override;
//...
end;
Now you can call a single class method aComponentClass.DoSomething;
in any case, whether the class is a composite class or not. The method "DoSomething" of the CompositeClass
for example calls "DoSomething" for every item of the list.
This allows you to ignore the difference between compositions of objects and individual objects.
A simple example for a composition is a directory - a directory is a composition of files. A simple implementation of a class that copies a file or a complete directory with all subdirectories looks like this:
uses Classes,SysUtils,..;
TFile = class
public
fName : string;
public
constructor Create(Name : string);
procedure Copy(DstDir : string); virtual;
property Name : string read fName;
end;
TDirectory = class(TFile)
private
FileList : TList;
public
constructor Create(Name : string);
destructor Destroy;
procedure Copy(DstDir : string); override;
property Name;
end;
// TFile
constructor TFile.Create(Name: string);
begin
fName:=Name;
end;
procedure TFile.Copy(DstDir: string);
var SrcFilename,DstFilename : string;
begin
SrcFilename:=fName;
DstFilename:=IncludeTrailingPathDelimiter(DstDir)+
ExtractFilename(fName);
if FileExists(SrcFilename) then
Windows.CopyFile(PChar(SrcFilename),PChar(DstFilename),false);
end;
// TDirectory
procedure TDirectory.Copy(DstDir: string);
var i : integer;
RelPath, Separator : string;
begin
if not DirectoryExists(DstDir) then
ForceDirectories(DstDir);
for i:=0 to FileList.Count-1 do
if TFile(FileList[i]) is tDirectory then
begin
RelPath:=ExtractRelativePath(IncludeTrailingPathDelimiter(Name), TDirectory(FileList[i]).Name);
Separator := '\'; //It's a filename separator
TDirectory(FileList[i]).Copy(DstDir+Separator+RelPath)
end else
TFile(FileList[i]).Copy(DstDir);
end;
constructor TDirectory.Create(Name: string);
var Root,s : string;
sr : tSearchRec;
begin
inherited Create(Name);
FileList := TList.Create;
Root:=IncludeTrailingPathDelimiter(Name);
s:=Root+'*.*';
if FindFirst(s, faAnyFile , sr) = 0 then
begin
repeat
if (sr.Name = '.') or (sr.Name = '..') then continue;
if ((sr.Attr and faDirectory) <> 0) then
FileList.Add(tDirectory.Create(Root+sr.Name))
else
FileList.Add(tFile.Create(Root+sr.Name));
until FindNext(sr) <> 0;
FindClose(sr);
end;
end;
destructor TDirectory.Destroy;
var i : integer;
begin
for i:=0 to FileList.Count-1 do
tFile(FileList[i]).Destroy;
FileList.Free;
end;