How to design constructors in a hierarchy where there are just readonly fields without having too many parameters? - delphi

I have designed a hierarchy, every class has 2 readonly properties mapped to 2 private fields.
Every class has a cosntructor that inherits the parent class one.
The problem is that at every level of hierarchy the number of parameters increase of 2:
TBaseClass.Create (par1, par2);
TSubClass.Create(par1, par2, par3, par4);
TSubSubClass.Create(par1, par2, par3, par4, par5, par6);
[...]
Is it ok to have constructors with 6-8 parameteres? After creation my objects should be immutable, so this is why I try to initialize all fileds in the constructors.
Is there another technique you can suggest or should I go with the above mentioned approach? Thanks.

As long as they're well-documented, I've never had any stigma against functions with large numbers of parameters. So an 8-param constructor wouldn't scare me.
However, I can see where the explosion-of-params could occur here, especially if you start adding more than 2 properties per object. I could also see an uncomfortable proliferation of constructor overloads, if some of those params can be defaulted/optional.
With that in mind, you might want to encapsulate the complexities of setting all those params, by using a construction pattern. Builder comes to mind, though Factory or Prototype might also be useful.

Best to stick with conventions when you can. A Builder implies a sequential or multiple object creation process. Abstract Factory is more appropriate for creating a single object from a hierarchy.
Having said that, the regularity does seem a little odd. Does SubClass3 in fact need all 6 properties or only it's two? Remember LSP - SubClass3 is supposed to be completely substitutible for BaseClass so at each level the new ancestor assumes responsibility for the entire set, which is usually more than just passing them back through constructors.

Why don't you let those properties be writable and protect the object by casting it to an interface? Like this:
type
IMyObject = interface
function GetProperty1(): integer;
function GetProperty2(): boolean;
end;
TMyObject = class(TInterfacedObject, IMyObject)
public
constructor Create();
function GetProperty1(): integer;
function GetProperty2(): boolean;
procedure SetProperty1(Value: integer);
procedure SetProperty2(Value: boolean);
end;
function CreateMyObject: IMyObject;
var obj: TMyObject
begin
obj := TMyObject.Create;
obj.SetProperty1(45);
obj.SetProperty2(false);
Result := obj;
end;

Related

Multiple generation of Obj with a generic class

I'm having some trouble with generics and constructors.
I would like to have a Generic class that can handle (and create) multiple objects of the same class. Moreover, I have some code that I would like to use whatever the specific class actually is.
I thought Generics are a good solution to this, but I'm not quite sure.
type
TMultiBlock<T: IBlock, constructor> = class(TObject)
blocks: array of array of T;
constructor Create(const owner: someClass, const n: integer);
end;
constructor TMultiBlock<T>.Create(const owner: someClass, const n: integer);
var
i: integer;
begin
for i := 0 to n-1 do
T.Create();
end;
The solution above works, but the Create() that is called is not the one of the class T that I give to the Generic.
I know that I can do the Create outside the TMultiBlock class, since I know class T there, as show below:
TM := TMultiBlock<TFinestra>.Create();
for i := 0 to n do
begin
TM.blocks[i] := TFinestra.Create();
end;
Here the class TFinestre is one of the class that I want to use in the Generic. But the thing is that I want to do some common operations on the T element, and these operation will be common to whatever the T type is, so I would like to do them on the TMultiBlock.
IBlock is an interface implemented by each class of type T.
An Interface is, in OOP terms, a pure abstract definition. It can be implemented by lots of different classes. You can't create objects from an interface (unless the interface gives you a method to create an object), you can get an interface from an Object if that object supports it. As all interfaces are implemented by a class then they can do different things, but ultimately what they do is dependent on what the object is, so you are back to having to know the class.
If you want to create the objects you have to know the class of the object you are creating. The interface could provide a method for returning the class of the object which is implementing the interface, allowing you to create more of them, but then you may as well use that class.
If your different types do not have a common ancestor, then you can specify, as you have, that T must support an interface, but you still instantiate the TMutliBLOCK<T> with a class. As interfaces are always implemented by a class then T will always derive from TObject so it will always support Create. The problem is you can't call T.Create unless IBlock includes the definition of Create ...
This means that you can have a TMulitBLOCK and a TMultiBLOCK but each would be holding the objects you have declared for it. If they're not inheriting from a common class then you would not be able to restrict the type of T to that common ancestor.
You can check that the type you are using supports an interface in the constructor, and then restrict to TObject.
TMultiBLOCK<T: class> = class(TObject)
protected
blocks: TArray<T>;
public
constructor Create(AOwner: pSomeObject; nSize: Integer);
end;
constructor TMultiBLOCK<T>.Create(AOwner: pSomeObject; nSize: Integer);
begin
if(not Supports(T, IBlock)) then
raise EInvalidCast.Create('Class '+T.ClassName+' must support IBlock')
else
...
end;
To call the members of IBlock you will need to get an interface pointer for each object. Bear in mind that depending on the implementation in the different implementing classes then when the interface references go out of scope the object may delete itself. To prevent that happening you probably want to store the interface references alongside the objects when you create them so the reference count is held above 0.
If you can organise the code so that all members are derived from a common ancestor then you can restrict your TMultiBLOCK<T> to that common ancestor, rather than a common interface.

How to free an object which is in a record?

Here I have a tricky situation, I guess. I need to be able to free an object which is a field of a record. I would normally write the cleanup code in the destructor, if it was a class. But since record types can't introduce a "destructor", how would it be possible to call TObject(Field).Free; ?
There'll be two types of usage I predict:
Replacing the record with a new one.
I think this usage would be easy to implement. Since records are value types and so they are copied on assignment, I can overload the assigning operator and free the objects owned by old record.
( Edit: Assignment overloading wasn't able. That's a new info to me.. )
Exiting the scope where record variable defined.
I can think of a private method that frees the objects and this method could be called on scope excitation manually. BUT, here is the same question: How to make it more recordly? This behaviour kind of feels like a class...
Here is a sample (and obviously not the intended usage):
TProperties = record
... some other spesific typed fields: Integers, pointers etc..
FBaseData: Pointer;
FAdditionalData: TList<Pointer>;
//FAdditionalData: array of Pointer; this was the first intended definition
end;
Assume,
FAdditionalData:=TList<Pointer>.Crete;
called in record constructor or manually in record variable scope by accessing the field publicly like
procedure TFormX.ButtonXClick(Sender: TObject);
var
rec: TProperties;
begin
//rec:=TProperties.Create(with some parameters);
rec.FAdditionalData:=TList<Pointer>.Create;
//do some work with rec
end;
After exiting the ButtonClick scope the rec is no more but a TList still keeps its existance which causes to memory leaks...
If all you have in the record is an object reference, then you can't get the compiler to help you. You are in sole charge of the lifetime of that object. You cannot overload the assignment operator, and you don't get any notification of scope finalisation.
What you can do though is to add a guard interface that will manage the lifetime of the object.
TMyRecord = record
obj: TMyObject;
guard: IInterface;
end;
You need to make sure that TMyObject manages its lifetime by reference counting. For example by deriving from TInterfacedObject.
When you initialise the record you do this:
rec.obj := TMyObject.Create;
rec.guard := rec.obj;
At this point, the guard field of the record will now manage your object's lifetime.
In fact, if you want to push this idea further, you can build a dedicated class to guard the lifetime of objects. That then no longer constrains you to implement IInterface on your class. There are plenty of examples on the web that illustrate the technique. For example I offer Jarrod Hollingworth's article titled Smart Pointers, and Barry Kelly's titled Reference-counted pointers, revisited. There are many more out there. It's an old trick!
Note however, that what you have here is a strange hybrid of value type and reference type. On the face of it, records are value types. However, this one acts like a reference type. If you have other fields in the record that are value types then that would be even more confusing. You'll need to be very aware of this issue when you work with such a record.
On the face of it, without knowing more about your design, I'd be inclined to advise you not to put object references in records. They fit better inside reference types, i.e. classes.
I remember that someone created a class named TLifetimeWatcher. Basically, it looks like:
TLifetimeWatcher = class(TInterfacedObject)
private
fInstance: TObject;
fProc: TProc;
public
constructor Create(instance: TObject); overload;
constructor Create(instance: TObject; proc: TProc); overload;
destructor Destroy; override;
end;
// The (cleanup) proc will be executed in the destructor if assigned, otherwise the instance will be freed by invoking the Free method.
https://docwiki.embarcadero.com/RADStudio/Sydney/en/Custom_Managed_Records
THeaderStruct = record
private
public
class operator Initialize (out Header: THeaderStruct);
class operator Finalize (var Header: THeaderStruct);
end;

Correct way to duplicate Delphi object

What are pros and cons of duplication an object instance with constructor or instance function?
Example A:
type
TMyObject = class
strict private
FField: integer;
public
constructor Create(srcObj: TMyObject); overload;
//alternatively:
//constructor CreateFrom(srcObj: TMyObject);
property Field: integer read FField;
end;
constructor TMyObject.Create(srcObj: TMyObject);
begin
inherited Create;
FField := srcObj.Field;
end;
Example B:
type
TMyObject = class
strict private
FField: integer;
public
function Clone: TMyObject;
property Field: integer read FField;
end;
function TMyObject.Clone: TMyObject;
begin
Result := TMyObject.Create;
Result.FField := FField;
end;
One major difference immediately springs to mind - in the latter case the Create constructor would have to be virtual so that a class hierarchy supporting Clone could be built basing on the TMyObject.
Assume that this is not a problem - that TMyObject and everything based on it is entirely under my control. What is your preferred way of doing copy constructor in Delphi? Which version do you find more readable? When would you use former or latter approach? Discuss. :)
EDIT:
My main concern with the first example is that the usage is very heavy compared to the second approach, i.e.
newObj := TMyObject.Create(oldObj)
vs.
newObj := oldObj.Clone;
EDIT2 or "Why I want single-line operation"
I agree that Assign is a reasonable approach in most cases. It's even reasonable to implement 'copy constructor' internally by simply using assign.
I'm usually creating such copies when multithreading and passing objects through the message queue. If object creation is fast, I usually pass a copy of the original object because that really simplifies the issues of object ownership.
IOW, I prefer to write
Send(TMyObject.Create(obj));
or
Send(obj.Clone);
to
newObj := TMyObject.Create;
newObj.Assign(obj);
Send(newObj);
The first adds information about which object to want to create, the second not. This can be used to instantiate e.g. a descendant or an ancestor of a class
The Delphi way (TPersistent) separates creation and cloning:
dest := TSomeClass.Create;
dest.Assign(source);
and has this same property that you explicitly choose the class to instantiate. But you don't need two constructors, one for normal use, and one where you want to clone.
edit due to oneline requirement
You can mix it of course using Delphi metaclasses (untested)
type
TBaseSomeObject = class;
TBaseObjectClass = class of TBaseSomeObject;
TBaseSomeObject = class(TPersistent)
function Clone(t: TBaseObjectClass = nil): TBaseSomeObject; virtual;
end;
...
function TBaseSomeObject.Clone(t: TBaseObjectClass = nil): TBaseSomeObject;
begin
if Assigned(t) then
Result := t.Create
else
Result := TBaseObjectClass(Self.ClassType).Create;
Result.Assign(Self);
end;
SendObject(obj.Clone); // full clone.
SendObject(obj.Clone(TDescandantObject)); // Cloned into Descendant object
For the rest, just implement your assign() operators, and you can mix multiple ways.
edit2
I replaced the code above with code tested in D2009. There are some dependencies of the types that might have confused you, hope it is clearer this way. Of course you'll have to study the assign mechanism. I also tested the metaclass=nil default parameter and it works, so I added it.
I don't think there is a correct way it just depend on personal style. (And as Marco pointed out, there are more ways.)
The constructor way is short but it violates the principle that the constructor must only construct the object. Which is possibly not a problem.
The clone way is short although you need to provide a call for each class.
The assign way is more Delphi like. It separates creation and initialization which is good because we like the one method one function concept that makes code better to maintain.
And if you implement Assign using streams, you have only one place to worry about which fields need to be available.
I like the clone style - but only in Java (or any other GC language). I used it some times in Delphi, but mostly I stay with Create and Assign, because it is much clearer who is responsible for the destruction of the object.
I use the second method, the one with the Clone function, and it works like a charm, even with complex classes. I find it more readable and error proof.

Casting between parent and child classes in delphi

I'm writing some software that targets two versions of very similar hardware which, until I use the API to initialize the hardware I'm not able to know which type I'll be getting back.
Because the hardware is very similar I planned to have a parent class (TParent) that has some abstract methods (for where the hardware differs) and then two child classes (TChildA, TChildB) which implement those methods in a hardware dependent manner.
So I would first instantiate an object of TParent check what kind it is then cast it to the correct child.
However when I do this and call one of the abstract methods fully implemented in the child class I get an EAbstractError.
e.g:
myHardware:=TParent.Create();
if myHardware.TypeA then
myHardware:=TChildA(myHardware)
else
myHardware:=TChildB(myHardware);
myHardware.SomeMehtod();
I'm assuming that I can't cast a Parent Class to a child class, and also that there's probably a better way of doing this. Any pointers?
You need a factory method to return you the correct class depending on the Type of hardware you are using...
function CreateHardware(isTypeA: Boolean): TParent;
begin
if IsTypeA then Result := TChildA.Create
else Result := TChildB.Create;
end;
...
var
myHardware: TParent;
begin
myHardware := CreateHardware(True);
myHardwarde.SomeMethod;
end;
... or you could use the State pattern.
Common in either approach is that your TParent class does not has the knowledge to determine the type of hardware.That knowlegde is transfered into the factory method, caller of the factory method, factory itself or state class.
Thanks to Binary Worrier and Mghie for pointing me in the right direction in this instance. The answer given by Lieven would be the easier way in cases where minimising the initialization of the hardware wasn't an issue.
The pImpl idiom is discussed elsewhere on SO
Here is how I understand the implementation in pseudo-delphi-code (note I've not bothered with public/private distinctions for this):
class TParent
procedure SomeMethod(); abstract;
end;
class TChildA (TParent)
procedure SomeMethod(); override;
end;
class TChildB (TParent)
procedure SomeMethod(); override;
end;
class THardware
HardwareSpecficMethods: TParent;
procedure SomeMethod;
constructor Create();
contrsuctor THardware.Create();
begin
InitializeHardware();
If Hardware.TypeA then
HardwareSpecificMethods:=TChildA.Create()
else
HardwareSpecificMethods:=TChildB.Create();
end;
procedure THardware.SomeMethod();
begin
HardwareSpecificMethods.SomeMethod();
end;
end; {class THardware}
You're right, you can't and shouldn't cast from base class to derived class.
I'm assuming you don't want to have the Child object re-run the Parent constructor?
If so . . .
Remove the Parent/Child relationship as it stands, you will have only one Hardware class.
For the specific ChildA and ChildB functionality, create a new inheritance pattern, so that you have an ISpecificHardwareTasks interface or base class, and two derived classes (SpecificA & SpecificB).
When Hardware is constructing it's self, and it gets to the point where it knows what type of hardware it's working with, it then creates an instance of SpecificA or SpecificB). This instance is private to Hardware.
Hardware exposes methods which wrap the ISpecificHardWareTasks methods (it can even implement that interface if that makes sense).
The Specific classes can take a reference to the Hardware class, if that's necessary (though I don't know if you have access to the this pointer in a constructor, my Delphi is getting rusty)
Hope these ramblings helped somewhat.

Interface "recursion" and reference counting

I have a small problem with interfaces. Here it is in Pseudo code :
type
Interface1 = interface
end;
Interface2 = interface
end;
TParentClass = class(TInterfacedObject, Interface1)
private
fChild : Interface2;
public
procedure AddChild(aChild : Interface2);
end;
TChildClass = class(TInterfacedObject, Interface2)
private
fParent : Interface2;
public
constructor Create(aPArent : Interface1);
end;
Can anyone see the flaw? I need the child to have a reference to it's parent, but the reference counting doesn't work in this situation. If I create a ParentClass instance, and add a child, then the parent class is never released. I can see why. How do I get round it?
A reference-counted reference has two semantics: it acts as a share of ownership as well as a means of navigating the object graph.
Typically, you don't need both of these semantics on all links in a cycle in the graph of references. Perhaps only parents own children, and not the other way around? If that is the case, you can make the child references to the parent weak links, by storing them as pointers, like this:
TChildClass = class(TInterfacedObject, Interface2)
private
fParent : Pointer;
function GetParent: Interface1;
public
constructor Create(aPArent : Interface1);
property Parent: Interface1 read GetParent;
end;
function TChildClass.GetParent: Interface1;
begin
Result := Interface1(fParent);
end;
constructor TChildClass.Create(AParent: Interface1);
begin
fParent := Pointer(AParent);
end;
This is safe if the root of the tree of instances is guaranteed to be kept alive somewhere, i.e. you are not relying on only keeping a reference to a branch of the tree and still being able to navigate the whole of it.
Well, the reference counting of course does work in this situation - it just doesn't solve the problem.
That's the biggest problem with reference counting - when you have a circular reference, you have to explicitely 'break' it (set one interface reference to 'nil', for example). That's also why reference counting is not really a replacement for garbage collection - garbage collectors are aware that cycles may exist and can release such cyclic structures when they are not referenced from the 'outside'.
You must make a method that explicitly unlinks the right references. There is no way to get the automatic reference counting working properly in this case.
With the use of a function pointer in the first example then the cyclic reference problem doesn't exist. .NET uses delegates, and VB6 uses events. All of which have the benefit of not incrementing the reference count of the object being pointed too.

Resources