Abstraction in Delphi - delphi

If I have child class,the child class inherits all methods from the parent,but how do I use functions from the child class in the parent class? Is that what abstraction is? How do I accomplish it?
My code:
type
cParent = class(TObject)
private
function ParentFunction:byte;
function ChildFunction:byte;virtual;abstract;
end;
type
cChild = class(cParent)
private function ChildFunction:byte;override;
end;
function cChild.ChildFunction:byte;
begin
Exit(20);
end;
function cParent.ParentFunction:byte;
begin
Exit(10);
end;
var
c:cParent;
begin
c:= cParent.Create;
WriteLn(c.ChildFunction);
Readln;
end.
It compiles file,but I get abstraction exception.

c:= cParent.Create;
WriteLn(c.ChildFunction);
You create an instance of cParent class here. This class does only contain an abstract ChildFunction that can be overridden by other classes. The function is not implemented in cParent, so you get an abstract error.
The code works if you use the cChild class instead, where ChildFunction is implemented:
c:= cChild.Create;
WriteLn(c.ChildFunction);
For clarification, imagine a parent class named GeometricObject with an virtual abstract method CalculateVolume. You can now create child classes like SphereObject or BoxObject that implement CalculateVolume by using the formula for spheres/boxes. But it doesn't make sense to create an instance of GeometricObject and call CalculateVolume.

You create instance of cParent class. This class don't have implementation of childFunction. c must be instance of cChild class.
Correct code:
c := cChild.Create;
WriteLn(c.ChildFunction);

Create the instance using the child class:
c:= cChild.Create;

It seems to me that you have confused inheritance relationships and possessive relationships. If you define a class "cChild" as inherited from class "cParent" then the class "cChild" is a "cParent", it does not mean, that the class "Parent" has access to any child classes. The only way you can call the abstract function "ChildFunction" in the class "cParent" is from another function, e.g. "ParentFunction", but only if the Object itself is not a "cParent":
function cParent.ParentFunction:byte;
begin
Result := ChildFunction * 2;
end;
var
c:cParent;
begin
c:= cChild.Create;
WriteLn(c.ParentFunction);
Readln;
end.
Here, since c is a cChild, ParentFunction correctly calls ChildFunction, which is defined in cChild.

In response to schnaader, regarding CS 101:
"virtual" means the method can be overridden by child classes.
Not only this, but non-virtual functions may happily be overridden by child classes. The difference is very important: when a virtual function is called by the parent class, the CHILD function will be executed. This allows child classes to implement functions that will be called by parent classes without the parent ever knowing anything about them. This is the essence of polymorphism.

Related

Delphi - Down casting object does not call base method

I have a base object type : TBServiceBookings and then I've derived another object type from that : TBServiceQuotes
So when my form is created I decide which object to use. In this instance I've created the derived object.
if fScreenType = ST_NewAppointment then
fBookingObject := TBServiceBookings.CreateServiceBookings(nil,botSingle)
else
fBookingObject := TBServiceQuotes.CreateServiceQuotes(nil,botSingle);
At some stage I want to call a method of the base class only. So I cast the derived object to the base type and call it's method, but it keeps on going to the derived method - which I don't want.
I've tried:
1) fBookingObject := TBServiceBookings(fBookingObject);
fBookingObject.SetupNewAppointmentScreen;
2)
TBServiceBookings(fBookingObject).SetupNewAppointmentScreen;
3)
(fBookingObject as TBServiceBookings).SetupNewAppointmentScreen;
What am I missing? Why does the derived method get called each time even though I've downcasted specifically to call the base method?
The only option I have left is to create a new variable of the base type and then carry on with that. But I already have a form variable which is my object, I just want to call a specific base class method.
Any help appreciated please!
Besides the question that lies behind your question "where do you need this unusual and somewhat suspicious construct for?", there are some possibilities to access an ancestor virtual method.
Really ugly: change the type of your component:
var
SaveType: TClass;
begin
SaveType := Self.ClassType;
PClass(Self)^ := TAncestor;
try
Self.AncestorMethod;
finally
PClass(Self)^ := SaveType
end;
end;
Cast the method instead of the class:
type
TInstanceMethod = procedure(Instance: TObject);
begin
TInstanceMethod(#TAncestor.AncestorMethod)(Self);
end;
Employ a class helper:
type
TAncestorHelper = class helper for TAncestor
procedure AncestorMethodViaHelper;
end;
procedure TAncestorHelper.AncestorMethodViaHelper;
begin
inherited AncestorMethod;
end;
begin
Self.AncestorMethodViaHelper;
end;
When in need, I myself always use the second solution. But that is only when dealing with ancestors I cannot change, e.g. the VCL. Within your own framework, you would never need these hacks because you can just redesign.
Well if the class is yours, you have full control, so just don't override the base method you want to call. Like :
fBaseObject.ThisMethodBase; { calls the original }
fDerivedObject.ThisMethod; { calls the new one }
Seems like the simplest way to do it. Also remember you can simply call the Inherited method from your overriden method. So if you want to get creative you can pass a boolean that indicates if you want the base functionality. Like :
type
TX1=class
function ThisMethod(whatever:Integer;callOldOne:Boolean=false):integer;virtual;
end;
TX2=class(TX1)
function ThisMethod(whatever:Integer;callOldOne:Boolean=false):integer;override;
end;
function TX1.ThisMethod(whatever:Integer;callOldOne:Boolean=false):integer;
begin
result:=1;
end;
function TX2.ThisMethod(whatever:Integer;callOldOne:Boolean=false):integer;
begin
if callOldOne then result:=inherited ThisMethod(whatever) else Result:=2;
end;
Object oriented programming is fun.

How to check if a method was implemented in class Delphi

I need to check if method was implemented in class.
My classes implements an interface.
Code.
IMyInterface = interface
['{538C19EB-22E3-478D-B163-741D6BB29991}']
procedure Show();
end;
TMyClassFormCustomer = class(TInterfacedObject, IMyInterface)
public
procedure Show();
end;
TMyClassFormVendors = class(TInterfacedObject, IMyInterface)
public
procedure Show();
end;
....
procedure TMyClassFormCustomer.Show;
var
Form: TFormCustomer;
begin
Form:= TFormCustomer.Create(nil);
try
Form.ShowModal();
finally
FreeAndNil(Form)
end;
end;
{ TMyClassFormVendors }
procedure TMyClassFormVendors.Show;
begin
end;
It is possible to check if method TMyClassFormVendors.Show have implementation?
When a method not have implementation I have disable item in menu
Thanks.
I see a couple of possibilities:
If your TMyClassFormVendors does not want to do anything in .Show then simply don't declare and implement IMyInterface. In your code you can then query for that interface with if Supports(MyClass, IMyInterface) and react accordingly (Disable Menu entry)
If your IMyInterface interface actually has more than one method declared, some of which are supported (I don't want to use the word implemented, as all methods have to be implemented) and others are not, then you should better split the interface into several different ones and proceed as described in 1
You could also declare and implement another interface "ICapabilities" that could have methods like CanShow etc. In your code you could then query your class like if (Myclass as ICapabilities).CanShow then ...
I personally would favour 2. as it is the cleanest approach IMO, but it depends on what you want to do specifically
You could use similar approach as it is used for handling the event methods. So what you want to do is instead of implementing the method directly into your class you add a property which can store a method pointer to the actual method you wanna use. And then at runtime you only check to see if the pointer to such method was already assigned or not.
The code for this would look something like this:
type
//Method type object
TMyMethod = procedure (SomeParameter: Integer) of object;
TMyClass = class(TObject)
private
//Field storing pointer to the method object
FMyMethod: TMyMethod;
protected
...
public
//proerty used for assigning and accesing the method
property MyMethod: TMyMethod read FMyMethod write FMyMethod;
end;
When you are designing the method objects you can define how many and which type of parameters you wanna use in that method.
Don't forget you need to assign method to your method property in order to use it. You can do this at any time. You can even unassign it by setting it to nil if needed.
You do this in same way as you do with event methods
MyClass.MyMethod := MyProcedure;
where MyProcedure needs to have the same parameter structure as the method object type you defined earlier.
And finally you can check to see if methods has been assigned at runtime using:
if Assigned(MyClass.MyMethod) then
begin
//Do something
end
else //Skip forward
EDIT: Another advantage of this approach is that your actual method that you use can be defined and written in completely different unit.

About different ways of calling a static class in delphi

I am creating a class with class constructor
TStaticDynSettings = class
public
class constructor create;
class destructor destroy;
class procedure Reload;
end;
Do all the other method in the class apart from create and destroy need to be a class methods?
I know for sure all the variables need to be a class var else the automatic initialization wont work.
what is the difference in calling the procedure as?..
var StDyn : TStaticDynSettings;`
StDyn.Reload;
and
TStaticDynSettings.Reload;
1 - If you are not planning to create object instances of TStaticDynSettings you can use any of 3 forms:
type
TStaticDynSettings = class
public
procedure Reload1;
class procedure Reload2;
class procedure Reload3; static;
end;
call examples:
procedure Call1;
var Instance: TStaticDynSettings;
begin
Instance.Reload1;
end;
procedure Call2;
begin
TStaticDynSettings.Reload2;
end;
procedure Call3;
begin
TStaticDynSettings.Reload3;
end;
The difference is a hidden argument passed in Reload1 and Reload2 methods and not passed in Reload3.
The hidden argument is a reference to object instance in Reload1 (and not used since you are not creating object instance), and a class reference in Reload2 (which probably also unnecessary in your case, assuming a class reference is known at compile time).
The first form (Reload1) is misleading because it assumes using an object reference and so should be avoided (though it works).
The third form (Reload3) is preferable if you are not using class references that are unknown at compile time.
2 - There is no difference (not counting an overhead of using unnecessary object variable if the first case).

Delphi Web Script: How to Expose a Class via RTTI which contains a Method returning another (exposed) Class

I have this Delphi class
type
TAnotherClass = class
end;
TMyClass = class
function Foo: TAnotherClass;
end;
function TMyClass.Foo: TAnotherClass;
begin
Result := TAnotherClass.Create;
end;
Now I'd like to expose this class via "dwsRTTIExposer.pas":
myUnit.ExposeRTTI(TypeInfo(TMyClass));
myUnit.ExposeRTTI(TypeInfo(TAnotherClass));
My Script looks like that:
var a: TMyClass = TMyClass.Create;
var b: TAnotherClass;
b := a.Foo;
Unfortunatelly Delphi Web Script doesn't recognize the return value from TMyClass.Foo as a valid Script Class. Is there a possibility to do that without falling back to manually expose each method with an OnEval-Eventhandler?
ExposeRTTI currently doesn't support parameters of class type.
This is because returning a direct Delphi class in the script can be problematic, as the life-cycle of Delphi objects is arbitrary and undetermined (the Delphi-side object can be destroyed at any time without notice f.i.).
You don't have to expose each method manually, you can use the RTTI exposer for every methods that involves basic types, and only have to deal manually with methods involving class types.
That'll then leave you with having to decide how you want the script-side objects to be exposed, and what their relationship to the Delphi-side object is, which is something the RTTI provides no clues about.
For instance with your original code, the OnEval code would just create a new script object that wraps the method Result for each call.
But the RTTI signature of Foo would still be exactly the same if its implementation was changed to something like
TMyClass = class
private
FFoo: TAnotherClass;
public
function Foo: TAnotherClass;
end;
function TMyClass.Foo: TAnotherClass;
begin
if FFoo=nil then
FFoo := TAnotherClass.Create;
Result := FFoo;
end;
However in that case, the OnEval would have to be completely different, as you would have to return the same script-side object on subsequent calls, and you would also need to hook the script-side object's destructor to properly handle the consequences on the private FFoo field.
Once Delphi has truly garbage-collected objects, the constraint could be relaxed, but currently the only thing that gets close is TInterfacedObject, which is unsafe, and you still have to deal with manual event handlers to handles things like circular references or classes that disable the reference counting (like the VCL components).

Calling member functions dynamically

I'm pretty sure it's possible to call a class and its member function dynamically in Delphi, but I can't quite seem to make it work. What am I missing?
// Here's a list of classes (some code removed for clarity)
moClassList : TList;
moClassList.Add( TClassA );
moClassList.Add( TClassB );
// Here is where I want to call an object's member function if the
// object's class is in the list:
for i := 0 to moClassList.Count - 1 do
if oObject is TClass(moClassList[i]) then
with oObject as TClass(moClassList[i]) do
Foo();
I get an undeclared identifier for Foo() at compile.
Clarification/Additional Information:
What I'm trying to accomplish is to create a Change Notification system between business classes. Class A registers to be notified of changes in Class B, and the system stores a mapping of Class A -> Class B. Then, when a Class B object changes, the system will call a A.Foo() to process the change. I'd like the notification system to not require any hard-coded classes if possible. There will always be a Foo() for any class that registers for notification.
Maybe this can't be done or there's a completely different and better approach to my problem.
By the way, this is not exactly an "Observer" design pattern because it's not dealing with objects in memory. Managing changes between related persistent data seems like a standard problem to be solved, but I've not found very much discussion about it.
Again, any assistance would be greatly appreciated.
Jeff
First of all you're doing something very unusual with TList: TList is a list of UNTYPED POINTERS. You can add any pointer you want to that list, and when you're doing moClassList.Add( TClassA ) you're actually adding a reference to the class TClassA to the list. Technically that's not wrong, it's just very unusual: I'd expect to see TClassList if you actually want to add a class! Or TList<TClass> if you're using a Delphi version that support it.
Then you're looping over the content of the list, and you're checking if oObject is of the type in the list. So you do want classes in that list after all. The test will work properly and test rather the object is of that type, but then when you do with oObject as TClass(moClassList[i]) do you're actually casting the object to... TObject. Not what you wanted, I'm sure!
And here you have an other problem: Using Foo() in that context will probably not work. TObject doesn't contain a Foo() method, but an other Foo() method might be available in context: That's the problem with the with keyword!
And to finally answer the question in the title bar: Delphi is not an Dynamic language. The compiler can't call a method it doesn't know about at compile time. You'll need to find a OOP way of expressing what you want (using simple inheritance or interfaces), or you may call the function using RTTI.
Edited after question clarification.
All your business classes need to implement some kind of notification request management, so your design benefits allot from a base class. Declare a base class that implements all you need, then derive all your business classes from it:
TBusinessBase = class
public
procedure RegisterNotification(...);
procedure UnregisterNotification(...);
procedure Foo;virtual;abstract;
end;
In your initial example you'd no longer need the list of supported classes. You'll simply do:
oObject.Foo;
No need for type testing since Delphi is strongly typed. No need for casting since you can declare oObject": TBusinessBase.
Alternatively, if you for some reason you can't change the inheritance for all your objects, you can use interfaces.
TClass is defined:
TClass = class of TObject;
You then write oObject as TClass which is effectively a null operation since oObject already was a TObject.
What you need is something like this:
type
TFoo = class
procedure Foo();
end;
TFooClass = class of TFoo;
TBar = class(TFoo)
procedure Bar();
end;
....
if oObject is TFooClass(moClassList[i]) then
with oObject as TFooClass(moClassList[i]) do
Foo();
This explains why your attempts to call Foo() does not compile, but I simply have no idea what you are trying to achieve. Even after your clarification I'm struggling to understand the problem.
Here's a really contrived example (using an array instead of a TList) that I think is what you're trying to do (error handling and try..finally intentionally omitted for clarity).
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TBaseClass=class(TObject)
procedure Foo; virtual;
end;
TClassA=class(TBaseClass)
procedure Foo; override;
end;
TClassB=class(TBaseClass)
procedure Foo; override;
end;
TClassArray= array of TBaseClass;
{ TClassB }
procedure TClassB.Foo;
begin
Writeln('TClassB.Foo() called.');
end;
{ TClassA }
procedure TClassA.Foo;
begin
Writeln('TClassA.Foo() called.');
end;
var
Base: TBaseClass;
ClassArr: TClassArray;
{ TBaseClass }
procedure TBaseClass.Foo;
begin
Writeln('TBaseClass.Foo called!!!!!!!!');
end;
begin
ClassArr := TClassArray.Create(TClassA.Create, TClassB.Create);
for Base in ClassArr do
Base.Foo;
for Base in ClassArr do
Base.Free;
ReadLn;
end.

Resources