This abstract factory pattern is based on that described in [Gam+, pages 87..95]. The Delphi implementation for the pattern is from Shaun Parry.
'To provide an interface for creating families of related or dependent objects without specifying their concrete classes' [Gam+,p87].
The motivation is based on [Gam+, page
87]. Consider a composite class which must contain the same
members but be created differently depending on the application.
For example, you could have a document which would be printed
differently depending on the printer being used, but would still
have to contain the same information. The idea behind abstract
factory is that you can pass a class (the factory) to the
constructor which has a standard interface. This interface is
then used by the constructor, but the methods it uses are
implemented differently for each kind of factory.
In the above class diagram, you can see how the Client Class
function, CreateProduct, uses the AbstractFactory classes'
CreateProduct function to create an object of the AbstractProduct
class. Because there is no direct connection between the Client
and the ConcreteProduct class, the ConcreteProduct can be any
derived class of AbstractProduct.
The following is an example of making a panel with three edit boxes within it. To do this we are goint to use a method called CreatePanel within a class we will create for the job called PanelMaker:
type
TPanelMaker = class (TObject)
public
function CreatePanel( aOwner :
TWinControl;
Factory : TAbstractFactory ): TPanel;
end;
implementation
function TPanelMaker.CreatePanel(aOwner : TWinControl;
Factory : TAbstractFactory): TPanel;
var
TempPanel : TPanel;
TempEdit1 : TEdit;
TempEdit2 : TEdit;
TempEdit3 : TEdit;
begin
TempPanel := Factory.MakePanel( aOwner );
TempEdit1 := Factory.MakeEdit( TempPanel, 'Test1', 10,
10 );
TempEdit2 := Factory.MakeEdit( TempPanel, 'Test2',
TempEdit1.Top + TempEdit1.Height + 10,
TempEdit1.Left );
TempEdit3 := Factory.MakeEdit( TempPanel, 'Test3',
TempEdit2.Top + TempEdit2.Height + 10,
TempEdit2.Left );
CreatePanel := TempPanel;
end;
Notice that we are not using TPanel.Create directly, but using the abstract factory method MakePanel instead. This makes it easier to change.
The abstract factory looks like this:
type
TAbstractFactory = class (TObject)
protected
FPanel : TPanel;
public
function MakeEdit( aOwner : TWinControl;
vText : string;
vTop : Integer;
vLeft : Integer ): TEdit; virtual;
function MakePanel( aOwner : TWinControl )
: TPanel; virtual;
end;
implementation
function TAbstractFactory.MakeEdit( aOwner : TWinControl;
vText : string;
vTop : Integer;
vLeft : Integer ): TEdit;
var
TempEdit : TEdit;
begin
TempEdit := TEdit.Create( aOwner );
TempEdit.Parent := aOwner;
TempEdit.Text := vText;
TempEdit.Top := vTop;
TempEdit.Left := vLeft;
MakeEdit := TempEdit;
end;
function TAbstractFactory.MakePanel( aOwner : TWinControl
) : TPanel;
var
TempPanel : TPanel;
begin
TempPanel := TPanel.Create( aOwner );
TempPanel.Parent := aOwner;
MakePanel := TempPanel;
end;
This is for standard edit boxes and panels, but say we want to have a panel with a special type of edit box which only has, say, uppercase characters in it.
type
TUppercaseEdit = class (TEdit)
protected
FOnChange : TNotifyEvent;
procedure Change; override;
function GetUppercase : string;
procedure SetUppercase( Value : string );
public
property Text : string read GetUppercase
write SetUppercase;
end;
You can then create a derivative of AbstractFactory which will use this edit box in place of the standard:
function TUppercaseFactory.MakeEdit( aOwner : TWinControl;
vText : string;
vTop : Integer;
vLeft : Integer ): TEdit;
var
TempEdit : TUppercaseEdit;
begin
TempEdit := TUppercaseEdit.Create( aOwner );
TempEdit.Parent := aOwner;
TempEdit.Text := vText;
TempEdit.Top := vTop;
TempEdit.Left := vLeft;
MakeEdit := TempEdit;
end;
Thus you don't have to change PanelMaker.CreatePanel.