Delphi Base Class Factory

last modified: November 12, 2007

Im not sure if this is a good pattern or not, or if you can do something simillar in Java, c plus plus etc.. But here goes

-SteveEyles

It could be better. You're using a switch on the type in a palce where you should be using virtual method calls.This also makes the base class dependent on all its subclasses.

As a matter of fact DelphiLanguage doesn't need the factory pattern because it has Factory, AbstractFactory and a few other DesignPatterns are in fact workarounds for languages with limited abilities. So here's how you can fix it:

Define a virtual constructor on the base class.
Define a collection indexed by TApeType (or even a string) that returns class of TApe
Call the virtual constructor: return myTypes[index].create(...)

This way you replace the case ... statement with a virtual call. It is slightly more elegant, and it allows for greater flexibility, you can make the factory mechanism. Delphi is quite unique among typed OO languages in the sense that it allows virtual constructors. --CostinCozianu


interface

type
TApeType = (atGorilla, atOrangutan, atChimp);

EUnknownApe = class(Exception);

TApe = class(TObject)
public
        class function CreateFactory(const ApeType : TApeType) : TApe;
end;

TGorilla = class(TApe);
TOrangutan = class(TApe);
TChimp = class(TApe);

implementation

class function TApe.CreateFactory(const ApeType : TApeType) : TApe;
begin
        case ApeType of
        atGorilla       : Result := TGorilla.Create;
        atOrangutan : Result := TOrangutan.Create;
        atChimp : Result := TChimp.Create;
        else
        raise EUnknownApe.Create;
        end;
end;

I can now dynamically create one of the 3 descendent classes without needing to see there interfaces, I only need to see the interface of TApe. (In actual use the 3 descendants would be in separate units). Putting 'class' in front of a method is the same as using 'static' in C plus plus


Some refactoring:

interface

type
  TApeType = (atGorilla, atOrangutan, atChimp);

TApe = class(TObject)
public
        class function CreateFactory(const ApeType : TApeType) : TApe;
end;

TApeClass = class of TApe;

TGorrila = class(TApe);
TOrangutan = class(TApe);
TChimp = class(TApe);

implementation

const theApeClasses: array[TApeType] of TApeClass = (TGorrila, TOrangutan, TChimp);

class function TApe.CreateFactory(const ApeType : TApeType) : TApe;
begin
   Result := theApeClasses[ApeType].Create;
end;

More refactoring:

interface

type

TApe = class(TObject)
public
        class function CreateApe(const ApeType : TApeType) : TApe;
end;

TApeClass = class of TApe;
TApeType = TApeClass;

TGorrila = class(TApe);
TOrangutan = class(TApe);
TChimp = class(TApe);

implementation

class function TApe.CreateApe(const ApeType : TApeType) : TApe;
begin
   Result := TpeType.Create;
end;

yet more refactoring:

TApe = class(TObject)
public
        constructor Create; virtual;
end;

Actually, the class factory should be really that: a class factory. It returns the class you want to instantiate, leaving instantiating up to you. You can design the getApsClass function to take a string parameter, or an int, or anything else you might desire -- it doesn't matter. It gives you a class of TApe you can call Create() on. I prefer this to returning an instantiated object.--ATS.

interface

type

TApe = class(TObject)
public
  constructor Create; virtual;
  class function getApeClass() : TApeClass;
end;

TApeClass = class of TApe;

TGorrila = class(TApe);
TOrangutan = class(TApe);
TChimp = class(TApe);

implementation

...



{somewhere in your code}}
TApe.getApeClass().Create();

Loading...