Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are Delphi interfaces inherited in subclasses

If I implement an interface on a base class will it be inherited by its sub classes, I know the functions/procedures will be, but I am more interested in whether I will be able to cast the subclass to the interface and then back to its original class.

What I am hoping I can do is pass objects of different base classes to a function, and then in the function determin there type and use them as appropriate.

Is this possible and is it the correct approach?

Update

to help clear up any confusion (or to create some more) here is what I would like to do (striped down to its core).

Interface

    IMyInterFace = interface
   ['{B7203E50-F5CB-4755-9DB1-FB41B7B192C5}'] 
     function MyFunction: Boolean;
   end;

Base Class

   type TMyObject = class(TInterfacedObject,IMyInterFace)

Sub Class

  type TSubMyObject = class(TMyObject)

Another Class

  type TMyOtherObject = class(TInterfacedObject,IMyInterFace)

Then the usage

 procedure foo();
   var obj:TSubMyObject;
 begin
      obj := TSubMyObject.Create();
      SendInterfaceObject(obj);
 end;

 procedure bar();
   var obj:TMyOtherObject;
 begin
      obj := TMyOtherObject.Create();
      SendInterfaceObject(obj);
 end;



 procedure SendInterfaceObject(iobj:IMyInterFace);
 begin
     if (iobj is TSubMyObject) then
     begin
        //code here
     end
     else if (iobj is TMyOtherObject) then
     begin
       //code here
     end;
 end;

Update 2

I have updated the code abit so show what I am after better.

the //code here sections have little to do with the object that are passed to it, for example if this class is TAccounts and it was passed a TEmployee object it may pay there weekly pay but if it was a TInvoice then it would check to see if it needed paying and only pay it when the date was 2 days before the dead line.

the TEmployee/TInvoice may even come from out side classes asking for payments to be made.

this is just an example.

like image 571
Re0sless Avatar asked Dec 10 '22 22:12

Re0sless


2 Answers

Yes, the interface is inherited by the subclass.

It's perfectly acceptable to cast from subclass to the interface.

However, and apologies if I'm reading your question wrong, but if "and then back to its original class" means . . .

You have Interface I, class A and class B. A implements I, and B inherits A, you possibly can, but REALLY SHOULD NOT cast from A to B.

EDIT:

You want to go from B to I and back to B . . . but you already have a reference to B, if B is what you pass to your function, so you don't need to cast from I to B (unless were talking about a different object, then No, don't do it)

Going from I to B is the same as going from A to B, you're trying to cast up the inheritance chain, which really is something you shouldn't do. Needing to do this is a code smell, it tells you that you should try and solve this problem in a different way (possibly by redesigning you classes (e.g. adding more properties / methods to I), or just deciding that the function will only work with the sub class - working with the subclass 'B' will give you access to all the methods of A & I).

Can you edit your question and add some sample code of what you're trying to do?

EDIT 2

 procedure SendInterfaceObject(iobj:IMyInterFace);
 begin
     if (iobj is TSubMyObject) then
     begin
        //code here
     end;
 end;

The 'If' statement in there is a bad idea, and breaks OO principals. If you need to do this then either

  • The interface definition is insufficient, you might want to add a Type property to the interface allowing you to (if iObj.SomeProperty = 1) then . . .)
  • The interface is simply not the correct soluton to this problem, and you should pass the reference as TSubMyObject.

EDIT 3:

@mghie: I agree with you, what I didn't explain very well was that SomeProperty has some data that allows the function to branch there, removing the dependancy of type checking. SomeProperty shouldn't 'simply' replace the type check (e.g. by putting the class name in a property, then checking the class name) That is indeed exactly the same problem.

There is some essential difference between Subclasses that inherit the interface. This difference should be expressed by either

  • Exposing some item of data that can then be used in the brach

e.g.

if(item.Color = Red) then 
   item.ContrastColor := Blue;
else
   item.ContrastColor := Red;
  • Or through polymorphism e.g.

IEmployee defines a CalculatePay method, TManager and TWorker implement IEmployee, each with different logic in the CalculatePay methods.

If the intent was to do something like the first case, polymorphism could be overkill (polymorphism doesn't fix every problem).

EDIT 4

You say "the //code here sections have little to do with the object that are passed to it . . ." I'm sorry but that statement is incorrect, if you need to Pay an Employee, you need to know their 1) EmployeeCode 2) Their Salary Details 3) Their bank details etc, if you're charging an invoice you need 1) InvoiceNumber 2) Invoice Amount 3) CustomerCode to charge to etc . . . this is an ideal place for Polymorphism.

Lets say the function taking the interface checks to see if "Accounts" needs to do something with the object (e.g. Pay the employee, charge an Invoice etc). So we might call the function AccountsCheck. Inside Accounts check you will have a peice of logic specific to each sub class (to pay an employee, to charge the invoice . . .) This is an ideal candidate for Polymorphism.

On you interface (or on another interface, or as a virtual method on the sub class) Define an "AccountsCheck" method. Then each derived class gets its own implementation of Accounts check.

The code moves out of your humungous single AccountsCheck function, and into smaller functions on each sub class. This makes the code

  • More obvious in intent (each class contains some logic for AccountsCheck)
  • You're less likely to break SubClass B's logic when fixing something in AccountsCheck for C
  • It's easier to figure out exactly what SubClass B's AccountsCheck logic is, you've only to check 20 lines of code in small AccountsCheck, not 200 in the General AccountsCheck)

There are more, "good reasons" for this, aif nyone wants to edit/post comments please do so.

If you find you need to share some logic between implementations of AccountsCheck, create some utility functions, don't reimplement the same wheel in each one.

Polymorphism is the solution to your problem.

like image 127
Binary Worrier Avatar answered Dec 21 '22 00:12

Binary Worrier


My suggestion here would be to not cast against the class but instead cast against another interface. Change your TMyOtherObject to:

type
  IOtherObjectIntf = interface
    ['{FD86EE29-ABCA-4D50-B32A-24A7E71486A7}']
  end;

type 
  TMyOtherObject = class(TInterfacedObject,IMyInterFace,IOtherObjectIntf)

and then change your other routine to read:

procedure SendInterfaceObject(iobj:IMyInterFace);
begin
  if Supports(iObj,IOtherObjectIntf) then
    begin
      //code here for TMyOtherObject
    end
  else 
    begin
      //code here for other standard implementations
    end;
end;

This way your "custom" code for the TMyOtherObject would also be applied to any of ITS descendants without any further custom code. The IOtherObjectIntf interface is used as nothing other than a "yep, I'm one of those" indicators which allows your code to branch properly. Sure, its laying waste to another Guid...but there are so many of them, who would notice? :)

like image 41
skamradt Avatar answered Dec 21 '22 00:12

skamradt