Class function that creates an instance of itself in Delphi - delphi

Can you have a class function that creates an instance of a class:
TMyClass = class(TSomeParent)
public
class function New(AValue : integer) : TMyClass;
end;
TDerivedClass = class(TMyClass)
public
function Beep;
end;
and then use it as follows
...
var
myList : TList<T>;
item : TDerivedClass;
begin
myList.Add(TDerivedClass.New(1))
myList.Add(TDerivedClass.New(3))
myList.Add(TDerivedClass.New(5))
for item in myList do
item.Beep; //times the count in the class function
...
And if so, what does that function code look like? Do you use TObject's NewInstance method and do you re-implement every-time for every derived class? Is it saver/better to use the Constructor?
The goal is to use this approach in a command pattern and load the command list with class types and a receiver e.g:
//FYI: document is an instance of TDocument
commandList.Execute(TOpenDocument(document));
commandList.Execute(TPasteFromClipboard(document));
//... lots of actions - some can undo
commandList.Execute(TPrintDocument(document));
commandList.Execute(TSaveDocument(document));
And the reason for this is that some commands will be specified via text/script and will need to be resolved at runtime.

What you're looking for is called the factory pattern. It can be done in Delphi; it's how the VCL deserializes forms, among other things. What you're missing is the registration/lookup part of the system. Here's the basic idea:
Somewhere, you set up a registration table. If you're on Delphi XE, you can implement this as a TDictionary<string, TMyClassType>, where TMyClassType is defined as class of TMyClass. This is important. You need a map between class names and class type references.
Put a virtual constructor on TMyClass. Everything that descends from it will use this constructor, or an override of it, when the factory pattern creates it.
When you create a new descendant class, have it call a method that will register itself with the registration table. This should happen at program startup, either in initialization or in a class constructor.
When you need to instantiate something from a script, do it like this:
class function TMyClass.New(clsname: string; [other params]): TMyClass;
begin
result := RegistrationTable[clsName].Create(other params);
end;
You use the registration table to get the class reference from the class name, and call the virtual constructor on the class reference to get the right type of object out of it.

Yes, it is technically possible to create an instance from a class method, simply call the actual constructor and then return the instance it creates, eg:
type
TMyClass = class(TSomeParent)
public
constructor Create(AValue : Integer); virtual;
class function New(AValue : integer) : TMyClass;
end;
TDerivedClass = class(TMyClass)
public
constructor Create(AValue : Integer); override;
function Beep;
end;
constructor TMyClass.Create(AValue : Integer);
begin
inherited Create;
...
end;
function TMyClass.New(AValue : integer) : TMyClass;
begin
Result := Create(AValue);
end;
constructor TDerivedClass.Create(AValue : Integer);
begin
inherited Create(AValue);
...
end;
var
myList : TList<TMyClass>;
item : TMyClass;
begin
myList.Add(TDerivedClass.New(1))
myList.Add(TDerivedClass.New(3))
myList.Add(TDerivedClass.New(5))
for item in myList do
TDerivedClass(item).Beep;
In which case, you are better off just using the constructor directly:
type
TMyClass = class(TSomeParent)
end;
TDerivedClass = class(TMyClass)
public
constructor Create(AValue : Integer);
function Beep;
end;
var
myList : TList<TDerivedClass>;
item : TDerivedClass;
begin
myList.Add(TDerivedClass.Create(1))
myList.Add(TDerivedClass.Create(3))
myList.Add(TDerivedClass.Create(5))
for item in myList do
item.Beep;

Can you have a class function that creates an instance of a class.
Is it saver/better to use the Constructor?
Constructor is a class function that creates an instance of class.
Just put:
constructor New(); virtual;
And you are good to go.
The virtual; part will let you call same New() constructor for all descendant classes.

Another option is to use RTTI. The code below runs as a normal method in my class as a way to get a new instance of the object with a subset of items, but as the items (along with the list object itself) are probably of descendent objects, creating an instance of the object in which the method is defined isn't good enough as it needs to be of the same type of the instance.
i.e.
TParentItem = Class
End;
TParentList = Class
Items : TList<TParentItem>;
Function GetSubRange(nStart,nEnd : Integer) : TParentList;
End;
TChildItem = Class(TParentItem)
end
TChildList = Class(TParentList)
end
List := TChildList.Create;
List.LoadData;
SubList := List.GetSubRange(1,3);
The implementation if GetSubRange would be something like...
Function TParentList.GetSubRange(nStart,nEnd : Integer) : TParentList;
var
aContext: TRttiContext;
aType: TRttiType;
aInsType : TRttiInstanceType;
sDebug : String;
begin
aContext := TRttiContext.Create;
aType := aContext.GetType(self.ClassType);
aInsType := aType.AsInstance;
Result := aInsType.GetMethod('Create').Invoke(aInsType.MetaclassType,[]).AsType<TParentList>;
sDebug := Result.ClassName; // Should be TChildList
// Add the items from the list that make up the subrange.
End;
I appreciate for some things it may be a bit OTT, but in the design above, it works and is another alternative, although I appreciate, its not a class method.

You should use a constructor (a special "kind" of class function). TObject.NewInstance is not a suitable option, unless you require special memory allocation.
And regarding the Execute routine of the command list: the action involved now depends on the type of the object. Imagine a document being able to open, print, paste and save at the same time (not a weird assumption), that would be difficult to implement in this structure. Instead, consider to add interfaces (IOpenDocument, IPasteFromClipboard, IPrintable, ISaveDocument) which indeed could all be actions of one document instance.

Related

How to use TObjectList for arbitrary class type?

I'm still a bit fuzzy with generics in Delphi, but have been using TObjectList<> quite widely. Now I have a situation where I have a base class with such a private field, but needs to be created for an arbitrary class, also inherited from another base.
To clarify, I have two base classes:
type
TItem = class;
TItems = class;
TItemClass = class of TItem;
TItem = class(TPersistent)
private
FSomeStuffForAllIneritedClasses: TSomeStuff;
end;
TItems = class(TPersistent)
private
FItems: TObjectList<TItem>;
FItemClass: TItemClass;
public
constructor Create(AItemClass: TItemClass);
destructor Destroy; override;
function Add: TItem;
...
end;
This pair of classes is then further inherited into more specific classes. I'd like the object list to be shared for all of them, while each holds actually a different type internally.
type
TSomeItem = class(TItem)
private
FSomeOtherStuff: TSomeOtherStuff;
...
end;
TSomeItems = class(TItems)
public
function Add: TSomeItem; //Calls inherited, similar to a TCollection
procedure DoSomethingOnlyThisClassShouldDo;
...
end;
Now the problem is when it comes to creating the actual object list. I'm trying to do it like this:
constructor TItems.Create(AItemClass: TItemClass);
begin
inherited Create;
FItemClass:= AItemClass;
FItems:= TObjectList<AItemClass>.Create(True);
end;
However, the code insight complains about this:
Undeclared Identifier AItemClass
Even more, the compiler has yet a different complaint:
Undeclared Identifier TObjectList
Where, I do in fact have System.Generics.Collections used in this unit.
What am I doing wrong here, and how should I do this instead?
Make TItems generic:
TItems<T: TItem, constructor> = class(TPersistent)
private
FItems: TObjectList<T>;
public
constructor Create;
destructor Destroy; override;
function Add: T;
...
end;
constructor TItems.Create;
begin
inherited Create;
FItems:= TObjectList<T>.Create(True);
end;
function TItems<T>.Add: T;
begin
Result := T.Create;
FItems.Add(Result);
end;
If you inherit, simply put the correct generic parameter:
TSomeItems = class(TItems<TSomeItem>)
public
procedure DoSomethingOnlyThisClassShouldDo;
...
end;
The TObjectList is not meant to be used in that manner. The fact that it was originally defined as TObjectList<TItem> means that it will expect you to create it this way as well. It needs to be defined with the precise class you intend to create it as.
Instead, just create it with TItem, and then whenever you create a new item which is supposed to be added to this list, then you create it using the class type. Any time you need to access the items in this list, just cast them on the fly.
For example...
Result:= FItemClass.Create;
FItems.Add(Result);
...can be the contents of your Add function.

Dynamically created object (providing its classname as a string) do not call its constructor

Here is the object:
TCell = class(TPersistent)
private
FAlignmentInCell :byte;
public
constructor Create; virtual;
published
property AlignmentInCell:byte read FAlignmentInCell write FAlignmentInCell;
end;
this is its constructor:
constructor TCell.Create;
begin
inherited;
FAlignmentInCell:=5;
end;
Here is a function, which dynamically creates any object derived form TPersistent (parameter is class name provided as a string)
function CreateObjectFromClassName(AClassName:string):TPersistent;
var DynamicObject:TPersistent;
TempObject:TPersistent;
DynamicPersistent:TPersistent;
DynamicComponent:TComponent;
PersistentClass:TPersistentclass;
ComponentClass:TComponentClass;
begin
PersistentClass:=TPersistentclass(FindClass(AClassName));
TempObject:=PersistentClass.Create;
if TempObject is TComponent then
begin
ComponentClass:=TComponentClass(FindClass(AClassName));
DynamicObject:=ComponentClass.Create(nil);
end;
if not (TempObject is TComponent) then
begin
DynamicObject:=PersistentClass.Create; // object is really TCell, but appropriate constructor seems to be not called.
end;
result:=DynamicObject;
end;
My idea is to create new Cell (TCell) like this:
procedure TForm1.btn1Click(Sender: TObject);
var p:TPersistent;
begin
p := CreateObjectFromClassName('TCell');
ShowMessage(IntToStr(TCell(p).AlignmentInCell)); // it is 0. (Why?)
end;
When I want to check AlignmentInCell property I get 0, but I expected 5. Why? Is there way to fix it?
This is similar to a recent question.
You use TPersistentClass. But TPersistent does not have a virtual constructor, so the normal constructor for TPersistent is called, which is the constructor it inherits from TObject.
If you want to call the virtual constructor, you will have to declare a
type
TCellClass = class of TCell;
Now you can modify CreateObjectFromClassName to use this metaclass instead of TPersistenClass, and then the actual constructor will be called.
Also, TempObject is never freed. And instead of is, I would rather use InheritsFrom.
I did not test the following, but it should work:
function CreateObjectFromClassName(const AClassName: string; AOwner: TComponent): TPersistent;
var
PersistentClass: TPersistentclass;
begin
PersistentClass := FindClass(AClassName);
if PersistentClass.InheritsFrom(TComponent) then
Result := TComponentClass(PersistentClass).Create(AOwner)
else if PersistentClass.InheritsFrom(TCell) then
Result := TCellClass(PersistentClass).Create
else
Result := PersistentClass.Create;
end;
The compiler can't know for sure what value your variable of type TPersistentClass will hold at run time. So he assumes that it is exactly that: a TPersistentClass.
TPersistentClass is defined as a class of TPersistent. TPersistent has no virtual constructor, the compiler will therefore not include a call to dynamically look up the address of the constructor in the VMT of the actual class, but a 'hard-coded' call to the only matching constructor TPersistent has: the one it inherits from its base class TObject.
It might be a decision with reasons I don't know, but if you had chosen to define TCell as following
TCell = class(TComponent)
private
FAlignmentInCell: byte;
public
constructor Create(AOwner: TComponent); override;
published
property AlignmentInCell:byte read FAlignmentInCell write FAlignmentInCell;
end;
you wouldn't need TempObject and all the decision making in your CreateObjectFromClassName function (and the possible leaks as pointed out by others):
function CreateObjectFromClassName(AClassName:string): TComponent;
var
ComponentClass:TComponentClass;
begin
ComponentClass:=TComponentClass(FindClass(AClassName));
Result := ComponentClass.Create(nil);
end;
And make sure to manage the Results life-time as it has no Owner.

Generic factory looping

I am trying to figure out how to write a generic factory in XE2. Lets say I have this:
type
TObjectTypes = (otLogger, otEmail);
type
TLoggerTypes = (lFile, lConsole, lDatabase);
type
TEmailTypes = (etPOP3, etSMTP);
Classes:
TSMTPEmail = class(TInterfacedObject, IEmail); // Supports emailing only
TPOP3Email = class(TInterfacedObject, IEmail); // Supports emailing only
TFileLogger = class(TInterfacedObject, ILogger); // Supports logging only
etc.
Now I do this to loop thru all TObjectTypes:
procedure TForm1.FormCreate(Sender: TObject);
var
_Interf: IInterface;
_Configuration: TDictionary<string, TValue>;
_ObjectType: TObjectTypes;
begin
_Configuration := nil;
_Configuration := TDictionary<string, TValue>.Create;
try
_Configuration.Add('FileLogFileName', '20160320.Log');
_Configuration.Add('SMTPEmailHost', 'mail.server.lt');
_Configuration.Add('POP3Server', 'some_server');
for _ObjectType := Low(TObjectTypes) to High(TObjectTypes) do
begin
_Interf := TTheFactory.Make(_ObjectType, _Configuration);
if Assigned(_Interf) then
begin
OutputDebugString(PWideChar((_Interf as TObject).ClassName));
if Supports(_Interf, IEmail) then
(_Interf as IEmail).Send('X');
if Supports(_Interf, ILogger) then
(_Interf as ILogger).GetLastErrorMsg;
end;
end;
finally
FreeAndNil(_Configuration);
end;
end;
So, I need a generic factory and be able to loop not thru all TObjectTypes, but thru all TLoggerTypes or thru all TEmailTypes and skip creating some e.g. lDatabase from TLoggerTypes or etPOP3 from TEmailTypes.
Factory should produce all kind of classes.
In Delphi making factories is pretty simple, thanks to metaclasses (class references), simple example of which is TClass:
TClass = class of TObject
In most cases, you should define your own abstract class for all factory members and metaclass for it:
TMyFactoryObject = class (TObject)
public
constructor FactoryCreate(aConfiguration: TConfiguration); virtual; abstract;
end;
TMyFactoryClass = class of TMyFactoryObject;
In this abstract class you can add some methods common for all descendants, in my example we have constructor which takes configuration as argument. How to react to it will be determined in descendants.
Then you declare descendant classes:
TMyLogger = class (TMyFactoryObject, ILogger)
private
...
public
constructor FactoryCreate(aConfiguration: TConfiguration); override;
... //implementation of ILogger interface etc
end;
TMyEmail = class (TMyFactoryObject, IEmail)
private
...
public
constructor FactoryCreate(aConfiguration: TConfiguration); override;
... //implementation of IEmail interface etc
end;
now you declare array of possible descendant classes:
var
MyFactory: array [otLogger..otEmail] of TMyFactoryClass;
and in initialization section or in other places you populate this array:
MyFactory[otLogger]:=TMyLogger;
MyFactory[orEmail]:=TMyEmail;
At last, TTheFactory.Make(_ObjectType, _Configuration); from your question can be replaced with:
MyFactory[_ObjectType].FactoryCreate(_Configuration);
and you'll get needed object as instance of type MyFactoryObject.
See http://docwiki.embarcadero.com/RADStudio/Seattle/en/Class_References for more information.

How i can determine if an abstract method is implemented?

I'm using a very large delphi third party library without source code, this library has several classes with abstract methods. I need to determine when an abtract method is implemented by a Descendant class in runtime to avoid the EAbstractError: Abstract Error and shows a custom message to the user or use another class instead.
for example in this code I want to check in runtime if the MyAbstractMethod is implemented.
type
TMyBaseClass = class
public
procedure MyAbstractMethod; virtual; abstract;
end;
TDescendantBase = class(TMyBaseClass)
public
end;
TChild = class(TDescendantBase)
public
procedure MyAbstractMethod; override;
end;
TChild2 = class(TDescendantBase)
end;
How I can determine if an abstract method is implemented in a Descendant class in runtime?
you can use the Rtti, the GetDeclaredMethods function get a list of all the methods that are declared in the reflected (current) type. So you can check if the method is present in the list returned by this function.
function MethodIsImplemented(const AClass:TClass;MethodName : string): Boolean;
var
m : TRttiMethod;
begin
Result := False;
for m in TRttiContext.Create.GetType(AClass.ClassInfo).GetDeclaredMethods do
begin
Result := CompareText(m.Name, MethodName)=0;
if Result then
break;
end;
end;
or you can compare the Parent.Name property of the TRttiMethod and check if match with the current class name.
function MethodIsImplemented(const AClass:TClass;MethodName : string): Boolean;
var
m : TRttiMethod;
begin
Result := False;
m:=TRttiContext.Create.GetType(AClass.ClassInfo).GetMethod(MethodName);
if m<>nil then
Result:=CompareText(AClass.ClassName,m.Parent.Name)=0;
end;
function ImplementsAbstractMethod(AObj: TMyBaseClass): Boolean;
type
TAbstractMethod = procedure of object;
var
BaseClass: TClass;
BaseImpl, Impl: TAbstractMethod;
begin
BaseClass := TMyBaseClass;
BaseImpl := TMyBaseClass(#BaseClass).MyAbstractMethod;
Impl := AObj.MyAbstractMethod;
Result := TMethod(Impl).Code <> TMethod(BaseImpl).Code;
end;
Look at the implementation of the 32-bit version of the TStream.Seek() method in the VCL source code (in Classes.pas). It performs a check to make sure the 64-bit version of Seek() has been overridden before calling it. It doesn't involve TRttiContext lookups to do that, just a simple loop through its Parent/Child VTable entries, similar to how Zoƫ's answer shows.

Prevent destruction of object passed by interface

i don't have much experience with interfaces in Delphi and Delphi at all.
Example:
IListModel = interface
function At(row, col : Integer) : String;
end;
MyModel = class(TInterfacedObject, IListModel)
public
function At(row, col : Integer) : String;
procedure ManipulateA;
procedure ManipulateBogus;
end;
There is a view that can visualize objects that implement the IListModel interface.
View = class(TForm)
public
constructor Create(model : IListModel); reintroduce;
end;
My app holds a MyModel instance
MyApp = class({...})
strict private
model : MyModel;
public
// ...
end;
In the app i create the model and work with it.
procedure MyApp.LoadModel;
procedure MyApp.OnFoo;
begin
model.ManipulateBogus;
end;
Now, i want to show the data
procedure MyApp.ShowModel;
var
v : View;
begin
v := View.Create(model); // implicit to IListView > refCount=1
v.ShowModal;
FreeAndNil(v);
// refCount = 0
// oops, my model is dead now
end;
I'm wondering what's the best way to solve this problem.
In MyApp i could hold both, the instance model : MyModel AND via IListModel interface.
Or I could introduce a new interface IMyModel and hold the model by this interface in the MyApp class. I had to use if Supports(...) in the ShowModel method to get the IListModel interface.
Or i derive the MyModel class from another non refcounting base class (TInterfacedPersistent or a self written class). Any other ideas?
What is the best way to work with interfaces in such situations?
Edit:
A non ref counting base class:
function NonRefCountingObject.QueryInterface(const IID: TGUID;
out Obj): HResult;
begin
if GetInterface(IID, Obj) then
Result := S_OK
else
Result := E_NOINTERFACE;
end;
function NonRefCountingObject._AddRef: Integer;
begin
Result := -1; // no reference counting
end;
function NonRefCountingObject._Release: Integer;
begin
Result := -1; // no reference counting
end;
Is this implementation ok?
If you want to use the reference counting that comes with interfaces, you should only reference that object through interfaces. No references to the object other than through interfaces and do not free the object yourself.
Or you could disable the reference counting by overriding _AddRef and _Release and destroy the object like you are used to. This is what TComponent does.
Or keep the reference counting, but call AddRef and Release when you reference it like an object.
edit
using a const parameter prevents reference count updating and speeds up your code:
constructor Create(const model : IListModel); reintroduce;
If you need both, Interfaces and object references, simply derive from TInterfacedPersistent (declared in Classes.pas) instead of TInterfacedObject. Be aware that you must assure that no interface reference is still hold when you free the instance.

Resources