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?

Original source