views:

888

answers:

5

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.

+8  A: 

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.

Binary Worrier
So how can I access methods in the subclass? What I wanted to to was go from B to I to B.
Re0sless
You can't cast A to B, but you can cast B to I and that was the question.
Gamecat
@Binary Worrier: A Type property is IMHO neither better nor worse than checking the class type itself. Polymorphism should eliminate the need for both.
mghie
@Binary Worrier: I agree completely with your points in EDIT 3. It's hard to say with the simplified example the OP gave whether object inheritance is necessary. But interfaces should work either way.
mghie
@edit 4, your right it does have to do with the passed object, I was juts trying to convey that it is code that belongs to accounts, it it may just pass the object on to a section in accounts that deals with payment and the object gets used there.
Re0sless
I think given the advice you have given here i need to rethink my original approach to this problem, thnaks
Re0sless
+1  A: 

The interface is inherited by the subclasses and you can cast the objects to the interface, but it is not safe(or recommended) to cast the interface to class. If you need to do this you are probably using interfaces the wrong way.

Tiago Moraes
+1  A: 

There seems to be some doubt on how your question is to be understood, and indeed in your comment to this answer you say that you want to "go from B to I to B".

This is indeed not recommended and only supported by using information about how interfaces are implemented on a class.

If I understand you correctly then what you want to do is to pass an interface to some method, and in that method do different things depending on what concrete class the interface was implemented by. It is however best to continue using interfaces once you start with them. You could let the interface have a method to return the implementing class, but you should not make any assumptions about what class the interface is implemented in - it costs you some of the benefits of programming against interfaces.

What you can do instead is create different interfaces, and implement some of them only in (some of) your descendant classes. Then you can use QueryInterface() or Supports() on the passed interface pointer. For your base class this will return nil, but for all descendant classes that implement the interface it will return a pointer that lets you call the methods only they have.

Edit: For example in the OmniThreadLibrary you will find:

IOmniWorker = interface
  ['{CA63E8C2-9B0E-4BFA-A527-31B2FCD8F413}']
  function  GetImplementor: TObject;
  ...
end;

which you could add to your interface. But again, IMHO the use of distinct interfaces is much better.

mghie
+1  A: 

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? :)

skamradt
A: 

You can't cast an interface to an object directly (it is not what interfaces are intended for) but sometimes it so practical to be able to do it, that you can't resist...

If you really want to do like that, you can use the example "IOmniWorker" given by mghie directly in IMyInterFace:

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

The function implementations look like that:

function TMyObject.GetImplementor: TObject;
begin
  Result := Self;
end;
function TMyOtherObject.GetImplementor: TObject;
begin
  Result := Self;
end;

SendInterfaceObject looks then like that:

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

By the way I have added a (very) small optimization: by passing iobj as "const" to the function you can avoid unnecessary reference counting.

Name