Reputation: 10886
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.
Upvotes: 4
Views: 2466
Reputation: 51711
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
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
e.g.
if(item.Color = Red) then
item.ContrastColor := Blue;
else
item.ContrastColor := Red;
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
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.
Upvotes: 9
Reputation: 3550
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.
Upvotes: 1
Reputation: 15538
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? :)
Upvotes: 2
Reputation: 32334
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.
Upvotes: 1
Reputation: 328
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.
Upvotes: 1