Only a minority of the Delphi developers knows that every Delphi developer uses a Factory pattern (delphi.about.com has an example in “regular” Delphi), but then implemented using virtual Create constructors.
So: time to shed some light on that :-)
Virtual constructors are to classes like virtual methods are like object instances.
The whole idea of the factory pattern is that you decouple the logic that determines what kind (in this case “class”) of thing (in this case “object instance”) to create from the actual creation.
It works like this using virtual Create constructors:
TComponent has a virtual Create constructor so, which can be overridden by any descending class:
type
TComponent = class(TPersistent, ...)
constructor Create(AOwner: TComponent); virtual;
...
end;
For instance the TDirectoryListBox.Create constructor overrides it:
type
TDirectoryListBox = class(...)
constructor Create(AOwner: TComponent); override;
...
end;
You can store a class reference (the class analogy to an object instance reference) in a variable of type ‘class type’. For component classes, there is a predefined type TComponentClass in the Classes unit:
type
TComponentClass = class of TComponent;
When you have a variable (or parameter) of type TComponentClass, you can do polymorphic construction, which is very very similar to the factory pattern:
var
ClassToCreate: TComponentClass;
...
procedure SomeMethodInSomeUnit;
begin
ClassToCreate := TButton;
end;
...
procedure AnotherMethodInAnotherUnit;
var
CreatedComponent: TComponent;
begin
CreatedComponent := ClassToCreate.Create(Application);
...
end;
The Delphi RTL uses this for instance here:
Result := TComponentClass(FindClass(ReadStr)).Create(nil);
and here:
// create another instance of this kind of grid
SubGrid := TCustomDBGrid(TComponentClass(Self.ClassType).Create(Self));
The first use in the Delphi RTL is how the whole creation process works of forms, datamodules, frames and components that are being read from a DFM file.
The form (datamodule/frame/…) classes actually have a (published) list of components that are on the form (datamodule/frame/…). That list includes for each component the instance name and the class reference.
When reading the DFM files, the Delphi RTL then:
- finds about the components instance name,
- uses that name to find the underlying class reference,
- then uses the class reference to dynamically create the correct object
A regular Delphi developer usually never sees that happen, but without it, the whole Delphi RAD experience would not exist.
Allen Bauer (the Chief Scientist at Embarcadero), wrote a short blog article about this topic as well.
There is also a SO question about where virtual constructors are being used.
Let me know if that was enough light on the virtual Create constructor topic :-)
Cobus Kruger said
I should also mention that when virtual constructors get combined with dynamically-loaded runtime packages and possibly the GetClass/FindClass methods, you have everything you need for a full plugin framework.
I work on a large system where the standard way of adding new functionality is basically to have some interface and a factory class in a core package, and to have package filenames stored in config. When the package loads, its contained classes can register themselves and be constructed when needed.
Of course there are other ways to do this that don’t require virtual constructors, but this mechanism has worked wonderfully for us for over a decade.
jpluimers said
Good point. The RTL support for components in the IDE is just that: a plugin framework, and I should have mentioned that. Thanks!
Günther The Beautiful said
Altough I believe I understand how virtual constructors work, I still don’t get why they’re necessary in the first place. You still need some decision what ClassToCreate is going to be. In your case it’s a TButton. Later, you’re using the virtual constructor of the superclass TComponent. Why not directly use the constructor of TButton? We’ve already made the decision it’s going to be a TButton. The only reason I can think of is making the decision what subclass to use and to actually create an instance in two different methods.
jpluimers said
Take for instance a
TYourButton
in aDFM
file. The VCL doesn’t know aboutTYourButtonCode.Create
, but it knows about . From the it knows the registeredTYourButton
class, and because is virtual the factory can create it.This all happens within the method’s local procedure CreateComponent: that is the actual factory (not-so-legal copy of code from the RTL).
There is a similar construction for VCL in Vcl.Forms.TApplication.CreateForm and, , and for FMX in .
The alternative would have been a
RegisterComponentConstructors
. That would have worked too, but the current implementation is more extendable.Günther The Beautiful said
I think I can see it now. Thank you!
jpluimers said
Thanks for asking. It helped me explain it better.