calling methods of a class inherited form an interface and another ancestor
delphi, interface, multiple-inheritance, oop
Solution
Implementing one or many interfaces is the correct approach, but looks like you're a bit confused about the proper syntax and have no experience with interfaces.
Basic things are:
- You have a common ancestor class (or form) for any number of classes. The ancestor declares virtual methods that the classes specialize in some way.
- Declare a Interface with any number of methods. Don't forget to add a GUID to your interface.
- Extend your form declarations to implement the declared Interface, adding the Interface to the class declaration and adding the declared methods.
- You now can:
- Polymorphically call any virtual method using the ancestor class
- Ask any form if it implements a interface, and if it does, retrieve a interface reference to it and call any interface method. All this can be done using the supports function.
I made the most simple example I can think off, using visual inheritance for forms and a simple interface. The extracts of the example follows:
The common ancestor class:
type
TServerForm = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
procedure Method1; virtual;
end;
The interface:
type
IMyInterface = interface
['{B7102C7E-F7F6-492A-982A-4C55CB1065B7}']
procedure Method2;
end;
The child forms:
This one inherits from the TServerForm and implements the interface
type
TServerForm3 = class(TServerForm, IMyInterface)
public
procedure Method1; override;
procedure Method2;
end;
This one just inherits from TServerForm
type
TServerForm4 = class(TServerForm)
public
procedure Method1; override;
end;
and this one inherits directly from TForm and implements the interface
type
TNoServerForm = class(TForm, IMyInterface)
public
procedure Method2;
end;
All the forms are auto-created and then, I have this code to call the methods on a couple of `OnClick` for buttons on another form (the main form of the application example):
To call the virtual method based on polymorphism:
procedure TForm6.Button1Click(Sender: TObject);
var
I: Integer;
begin
for I := 0 to Screen.FormCount - 1 do
begin
if (Screen.Forms[I] is TServerForm) then
TServerForm(Screen.Forms[I]).Method1;
end;
end;
To call the method based on the implementation of the interface:
procedure TForm6.Button2Click(Sender: TObject);
var
I: Integer;
Intf: IMyInterface;
begin
for I := 0 to Screen.FormCount - 1 do
begin
if Supports(Screen.Forms[I], IMyInterface, Intf) then
Intf.Method2;
end;
end;
The methods just show messages, and everything works fine:
procedure TServerForm3.Method2;
begin
ShowMessage(ClassName + '.Method2');
end;
Problem
I have problem with implementing relation between some classes. I got three different classes serving three different forms. All these classes speak in same language, so they are inherited from same class called `TAncestorServer`. Let’s call its descendants `TForm1Server`, `TForm2Server`, and `TForm3Server`. `TAncestorServer` contains an abstract method called `Function1`, so `TForm1`, `TForm2`, and `TForm3` can call it with no problem through their own classes inherited from it, they have an access to the classes through a property named `Server`. But the problem is about another form called `TForm4`! It’s very similar to other forms but it’s not independent like them. It works with `TForm2Server` or `TForm3Server`. Still not a problem, but imagine another method like `Function2` which is declared in `TForm2Server` and `TForm3Server`, `TForm4` needs to call them. I can go like this: ``` if Server is TForm2Server then TForm2Server(Server).Function2 else if Server is TForm3Server then TForm3Server(Server).Function2; ``` But it can turn in an infinite if-else clause! So I thought something like multiple-inheritance may help here. I declared an interface called `IForm4Server` which contains `Function2`. So `TForm2Server` and `TForm3Server` inherit from both `TAncestorServer` and `IForm4Server`. I thought something like this could work: ``` If Server is IForm4Server then IForm4Server(Server).Function2; ``` But compiler doesn’t think so, it says that it’s not a valid type cast, because `TAncestorServer` is not `IForm4Server`, which is absolutely right. `TForm1Server` has no idea about implementing `Function2` and has to leave it blank. I can’t declare `TForm4.Server` as `IForm4Server` too, because `Function1` represents a huge number of methods and properties, however I still can’t type cast `IForm4Server` to `TAncestorServer`. As a solution I can define two different properties in `TForm4` like `GeneralServer: TAncestorServer` and `Form4Server: IForm4Server` and then assign same instance of `TForm2Server` or `TForm3Server` to them, but I don’t feel good about it. How should I do this? Is there any standard pattern for it?