In this question you see that is possible to create an abstract class adding the abstract keywrod. I am translating a project in Delphi but I see that it allows the creation of an abstract class. This is the code:
type
TCableSPF = class abstract
//code
end;
This is an abstract class of course and I have a lot of subclasses that implement it. By the way I see that is it possible to create an instance of this like;
a := TCableSPF.Create;
When I try to call its methods that are virtual and abstract I get the error and this is ok but can I prevent the creation of the class? Or Delphi allows this by default? thanks for the help
The class abstract is a holdover from the Delphi for .Net days.
For reasons unknown there is no (current) implementation behind this keyword.
If you want to prevent instantiation of an abstract class, this keyword will not help. Instead do the following:
type
TCableSPF = class abstract
//code
strict protected
//Define all constructors for an abstract class as protected.
constructor Create; virtual; reintroduce;
end;
By defining all constructors as protected, only descendent object can access the constructor, other code cannot access the constructor.
Because you reintroducing the virtual constructor, you also cannot instantiate it using:
unit A;
type
TMyAbstractObject = class abstract(TObjectX)
strict protected
constructor Create; virtual; reintroduce;
end;
...
unit B;
TMyClass = class of TObjectX;
AbstractObjectInstance = TMyClass.Create; //Will not work for TMyAbstractObject
Note that you should not declare the constructor override. Instead declare it virtual reintroduce (or just reintroduce if you don't want to allow virtual constructors).
Related
The one with virtual/dynamic
// declare in Child Class
constructor Create; virtual;
constructor TChildClass.Create;
begin
inherited;
end;
The one with override.
// declare in Child Class
constructor Create; override;
constructor TChildClass.Create;
begin
inherited;
end;
The one with nothing
// declare in Child Class
constructor Create;
constructor TChildClass.Create;
begin
inherited;
end;
Are these the same thing? It looks confusing.
Yes, there is a difference, but let's deal with the virtual keyword in more basic OOP terms first, yet still how it applies to Delphi methods.
When you declare a derived (child) class, and implement a method as "override", it means that you're overriding (surprise) the matching method of the base class.
This means that you can write code like this:
var child : TBaseClass;
begin
child := TChildClass.Create; // note that it is stored in TBaseClass variable
child.VirtualMethodDefinedInBaseClassThatHasBeenOverriddenInChildClass;
This will call the method in the child class, even if the variable is defined to be of the base class type. This is the whole purpose of virtual methods, you can access the object through a reference of a more general type, and still call methods that have been written for the particular type of object you're dealing with.
If you have a virtual method in the base class that you chose not to override in the child class, but instead reintroduce, you're effectively replacing it in some cases. Note that in most cases you need to tell the compiler that you really meant to do this, though I'm unsure about what Delphi requires here.
Basically, if your variable is of type TBaseClass, and you call a virtual method on it, that has been reintroduced in the TChildClass, it will still call the method in the base class.
However, if your variable is of type TChildClass, and you call that method on it, you will get the new method instead.
Now, for constructors, in Delphi, it is slightly different.
The point of virtual constructors is to be able to virtually construct objects, and to do that, Delphi also has "class types".
You can say this:
type TClassToUse = class of TBaseClass;
var cls : TClassToUse;
obj : TBaseClass;
begin
cls := TChildClass;
obj := cls.Create;
(note that my Delphi knowledge is a bit rusty here, if anyone spots bugs or glaring problems in the above code, please let me know or just fix it)
Here we store a "class" in a variable, and then ask the class to please construct an object for us. This allows us to switch out which class to create, but we also need to declare the constructors we want to use virtual, otherwise we will have problems.
So in the above code, if you declared the constructor as virtual in TBaseClass, and then override it in TChildClass (which the code is actually using in cls), the overridden constructor is the one that will be used.
If, on the other hand, you don't declare the constructor as virtual, we're back to the base class constructor. Virtual basically means to figure out the right method to execute at runtime, whereas non-virtual will figure it out at compile time.
Reintroduction as described for normal methods above, also works this way.
However, virtual constructors are only used as virtual when used through a class type.
No, static and virtual methods are not the same thing.
And override is a case of virtual method.
http://en.wikipedia.org/wiki/Virtual_function
http://docwiki.embarcadero.com/RADStudio/XE4/en/Methods#Method_Binding
Constructors bring nothing special here - they conform to the same rules as other methods for the question
have simple object hierarchy like below
TLiveThing=class
protected
FTest:string;
constructor Create(whereLive:string);overload;virtual;
constructor Create(haveBone:Boolean);overload;virtual;
end;
THuman=class(TLiveThing)
public
constructor Create(whereLive:string);overload;override;
constructor Create(age:integer);overload;
end;
in theoretically, if I instantiate THuman, I have 2 constructor, but in fact I have 5 constructor displayed by code insight, actually I want to see 3 constructor,
- Create(whereLive:String), overriden
- Create(age:integer)
- Create(haveBone:integer)
human:=THuman.Create( <=====in there I have 5 suggestion constructor
why I have this strange behaviour? how to fix it, because it so anoying, I cant always check my class that I need to instantiate, and if I instantiate like below
human:=THuman.Create(); <===== it doesnt give me error
how I completely hide my anchestor constructor? , because if I instatiate like above, completely give me a wrong object
UPDATE: and also I can see default Create without parameter from TObject too
Without putting focus on your bad constructor implementation,
your problem is that both the ancestor and the child class are defined in the same unit, therefor the standard definition of Private/Protected does not apply here.
If you want to prevent the ancestor constructor (which you are overriding in the child class) from showing up as a code parameter when instantiating an object of that derrived class then simply make it a member of the strict protected or strict private section.
With your example :
TLiveThing=class
strict protected
constructor Create(whereLive:string); virtual;
end;
THuman=class(TLiveThing)
public
constructor Create(whereLive:string); overload; override;
constructor Create(age:integer); overload;
end;
This will prevent the ancestor constructor Create(whereLive:string) from showing up as a parameter when you are creating an instance of your child class.
As pointed out by David, this has nothing to do with hiding the default Create constructor, it's only viable for hiding your custom constructors.
Let's assume you have a very well designed Delphi project, which respects the dependency injection and some other good practices.
Now let's assume you need to mock a class defined as:
TMyClass = class
public
procedure Method1;
procedure Method2
end;
Method1 and Method2 are not virtual. What do you do in this case? To mock an object we need to inherit it and override every method you want to mock, but it is not possible in this case because they are not virtual. Should I change the source code to add virtual on every method I need to mock? Is not it bad?
Edit
I was thinking about creating a compiler directive to make all fields in the class to be virtual, is it a good ideia? Only my test suite is going to set the compiler directive.
EDIT2*
Embarcadero should provide an easy way of changing a method pointer of a class to another method point, without the need for virtual.
Make the methods virtual so you can mock them. (They don't need to be abstract.)
If you cannot do that, then wrap the class with another class. Make the wrapper's methods virtual, and in the default implementation, just forward call calls to the original class. Wherever your program uses the original class, replace it with the wrapper. Now, mock the wrapper.
To mock an object we need to inherit
it, but it is not possible in this
case.
I'd recommend that every class that you say "I need to mock this" should end up being based on an interface.
In other words, if you have methods that need to be mocked, they should be put into an interface, and then the class to be mocked implements that interface. Then, you create the mock by implementing the same interface in the mock object.
The alternative is to use a Mocking library. You can look at these SO question:
What is your favorite Delphi mocking library?
and this framework includes mocking objects as well:
http://code.google.com/p/emballo/
Mocking objects should also encourage the proper use of dependency injection in your code.
You should not only make the methods virtual, you should declare a pure virtual base class, and make the other classes use only the pure virtual base class name. Now you can use what we call the "Liskov substitution principle", and you can make as many concrete subtypes of the abstract base class as you need. Since the abstract base class works just like an Interface works in delphi, minus the reference counting part, you get the best of both worlds. You can really keep your app code simple, and reduce bad coupling this way, plus you get unit testable "composite objects" that you put together yourself.
// in UnitBase.pas
TMyClassBase = class
public
procedure Method1; virtual; abstract;
procedure Method2; virtual; abstract;
end;
// in UnitReal.pas
TMyClassReal = class(TMyClassbase)
public
procedure Method1; override;
procedure Method2; override;
end;
// in UnitMock.pas
TMyClassMock = class(TMyClassbase)
public
procedure Method1; override;
procedure Method2; override;
end;
In the place that uses "TMyClass", change it to use TMyClassbase:
TMyOtherClass = class(TMyOtherClassBase)
private
FMyThing:TMyClassbase;
public
property MyThing:TMyClassBase read FMyThing write FMyThing;
end;
By connecting TMyOtherclass at runtime you can decide whether to use the real or mock class:
// in my realapp.pas
MyOtherClassObj := TMyotherClass.Create;
MyOtherClassObj.MyThing := TMyOtherClassReal.Create; // real object
// in my unittest.pas
MyOtherClassObj := TMyotherClass.Create;
MyOtherClassObj.MyThing := TMyOtherClassMock.Create; // mock object
(Don't forget to make a way to free MyOtherClassObj.MyThing later, or you'll have a leak)
I have two classes, a base class and a derived class. My base class has a constructor of this form:
constructor TBaseClass.CreateFromXML(ANode: IXMLNode);
begin
Create;
//Set members from XML
end;
My derived class has a constructor of this form:
constructor TDerivedClass.Create;
begin
FDatabaseID = -1;
end;
My problem is that when I create an object of my derived class using the constructor from the base class [ TDerivedClass.CreateFromXML(Node); ] the Create called at the beginning of the CreateFromXML constructor is not the one from my derived class, but rather the one inherited by my base class from TObject.
Is it possible to get the base class constructor to call my derived class constructor even though it's further "down" the inheritance chain?
Try declaring a constructor Create; virtual; in TBaseClass. Don't forget to mark the "derived" constructor as override.
If I write
type
MyClass = class of TMyClass;
...
Obj := MyClass.Create;
the correct constructor (the one in TMyClass) is called.
If I write
var
ClassVar : TClass;
...
ClassVar := TMyClass;
Obj := ClassVar.Create;
only the TObject constructor is called.
Why? What's the difference between the two versions? Can I force the TMyClass constructor call in the second scenario?
TClass is declared as "Class of TObject" in system.pas. What constructor gets called is decided at compile-time, and all the compiler knows is what base class you're using. It doesn't know what the value of the variable will be when it runs, so it has to default to the base class. If you're using a TClass, then your base class is TObject.
If you're using a class variable, I assume you have some sort of heirarchy and you're trying to implement a factory. If you want to make sure the right constructor gets called based on the value of a class variable at runtime, not something contained in your code at compile time, you need a virtual constructor.
type
TMyBaseObject = class(TObject)
public
constructor Create; virtual;
end;
TMyClass = class of TMyBaseObject;
Use a TMyClass instead of a TClass as your class variable, and now the compiler will generate a call to TMyBaseObject.Create, which is virtual. Make sure all your derived classes override the base constructor, and you'll end up calling the right one at runtime.
TObject.Create is not virtual, you need to declare ClassVar as a classtype of a class with a virtual constructor.
I would suggest you look into overriding the AfterConstruction method that's introduced by the TObject to make polymorphism like this work.
Each class definition can introduce a new constructor, with it's own set of parameters. A class variable only knows of the constructor if the base class it's a variable for. This is all possible because a constructor is neither virtual nor overridden. You could flag the constructor virtual on your base-class, and flag all descending classes override, which blocks the parameter list. (I think if you forget 'override' the compiler warns your new constructir hides the virtual one.)
Or descend it from TComponent, which already have a virtual constructor.