Delphi interfaces inherited in subclasses

If I implement an interface in a base class, it will be inherited by its subclasses, I know that there will be functions / procedures, but I'm more interested in whether I can subclass the interface and then return to the original class.

What I hope I can do is pass objects from different base classes to a function, and then enter them in the definein function and use them as needed.

Is this possible, and is this the right approach?

Update

to help eliminate any confusion (or create a few more), here is what I would like to do (striped 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 use

  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 updated the abit code to show that I am better.

the sections of the section here have little to do with the object that is passed to it, for example, if this class is TAccounts and the TEmployee object is passed to it, it can pay weekly payment, but if it was TInvoice, then it will check whether you need to pay and pay only. when the date was 2 days before the dead line.

TEmployee / TInvoice may even come from third-party classes asking you to make payments.

This is just an example.

+4
source share
5 answers

Yes, an interface is inherited by a subclass.

This is perfectly acceptable for translation from a subclass to an interface.

However, and apologies if I read your question incorrectly, 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 probably can, but REALLY DO NOT throw from A to B.

EDIT:

You want to go from B to Z and back to B., but you already have a link to B if B is what you pass to your function, so you don’t need to drop from I to B (unless you talked about something else object, then No, do not do this)

The transition from I to B is the same as the transition from A to B, you are trying to transfer the chain of inheritance, which you really should not do. The need for this is the smell of code, it tells you that you should try to solve this problem differently (perhaps redesigning your classes (for example, adding more properties / methods to I) or just deciding that the function will work only with the subclass - work with subclass “B” will give you access to all methods A and I).

Can you change your question and add sample code for what you are trying to do?

EDIT 2

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

The statement “If” has a bad idea and breaks the principles of OO. If you need to do this, then

  • Defining an interface is not enough, you can add Enter a property into the interface allowing you (if iObj.SomeProperty = 1), then.,.)
  • The interface is simply not the right solution for this problem and you must pass the link as TSubMyObject.

EDIT 3:

@mghie: I agree with you that I did not explain very well that SomeProperty has some data that allows the function to be debugged there, removing the type-checking dependency. SomeProperty should not simply replace type checking (for example, by putting the class name in a property and then checking the class name). This is really the exact same problem.

There is a significant difference between subclasses that inherit from an interface. This difference must be expressed either

  • Providing some data item that can then be used on the shoulder

eg.

 if(item.Color = Red) then item.ContrastColor := Blue; else item.ContrastColor := Red; 
  • Or through polymorphism, for example

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

If the goal was to do something like the first case, polymorphism may be excessive (polymorphism does not solve every problem).

EDIT 4

You say that the sections here // have nothing to do with the object that was passed to him ... I'm sorry, but this expression is incorrect, if you need to pay an employee, you need to know them 1) EmployeeCode 2) Their salary data 3) Their bank details, etc. If you charge the account that you need 1) InvoiceNumber 2) The invoice amount 3) CustomerCode for payment, etc., this is an ideal place for polymorphism .

Suppose that the function that performs the interface check checks whether the Accounts need to do something with the object (for example, pay an employee, pay an invoice, etc.). So we could call the AccountsCheck function. Inside the account verification, you will have your own logic specific to each subclass (pay for an employee, charge an invoice). This is an ideal candidate for polymorphism.

On your interface (or on another interface or as a virtual method on a subclass) Define the "AccountsCheck" method. Then each derived class gets its own account verification implementation.

The code moves from your simple AccountsCheck function to smaller functions for each subclass. It does the code

  • More obvious in intent (each class contains some logic for AccountsCheck)
  • You are less likely to break SubClass B when committing anything in AccountsCheck for C
  • It’s easier to understand what exactly is the logic for checking the SubClass B account, you should check only 20 lines of code in Small AccountsCheck, and not 200 in the General account)

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

If you find that you need common logic between AccountsCheck implementations, create some utility functions, do not redefine the same wheel in each of them.

Polymorphism is the solution to your problem.

+9
source

My suggestion here would be to not throw against the class, but instead use 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 procedure as follows:

 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; 

Thus, your “custom” code for TMyOtherObject will also apply to any of the descendants of ITS without any additional code. The IOtherObjectIntf interface is used as nothing more than a “yes, I am one of those” indicators that allows your code to enter correctly. Sure, its laying waste to another Guide ... but there are so many who would notice? :)

+2
source

An interface is inherited by subclasses, and you can pass objects to an interface, but it is unsafe (or recommended) to pass an interface to a class. If you need to do this, you are probably using the interfaces incorrectly.

+1
source

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

This is really not recommended and is only supported when using information about how interfaces are implemented in the class.

If you understand correctly, then what you want to do is pass the interface to some method, and do different things in this method depending on which particular class the interface implemented. However, it is best to continue to use interfaces as soon as you start with them. You could let the interface have a method for returning the implementation class, but you should not make any assumptions about which class is used in the interface - this costs you some of the benefits of programming from interfaces.

Instead, you can create different interfaces and implement some of them only in (some) of your descendant classes. Then you can use QueryInterface () or Supports () in the pointer of the passed interface. For your base class, this will return nil, but for all descendant classes that implement the interface, it will return a pointer that allows you to call only those methods.

Edit: For example, in the OmniThreadLibrary you will find:

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

which you can add to your interface. But then again, IMHO using different interfaces is much better.

+1
source

You cannot directly use the interface for an object (this is not what the interfaces are for), but sometimes it is so practical that you can do it that you cannot resist ...

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

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

Function implementations are as follows:

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

SendInterfaceObject looks like this:

 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 added a little optimization: passing iobj as "const" to the function, you can avoid unnecessary reference counting.

+1
source

All Articles