Multiple Interface inherited Delphi

Viewed 171

I have the code below. IAnimal is the base interface of all animal on my application.

Why I can't declare var with the the type I want, get a object that implement the base interface IAnimal and call the method ?

type
  IAnimal = interface
  end;

  ICat = interface(IAnimal)
    procedure Hunt;
  end;

  IBird = interface(IAnimal)
    procedure Fly;
  end;

  TCat = class(TInterfacedObject, ICat)
    procedure Hunt;
  end;

  TBird = class(TInterfacedObject, IBird)
    procedure Fly;
  end;

  TAnimalType = (atCat, atBird);

  TAnimalFactory = class
    class function GetAnimal(aType: TAnimalType): IAnimal;
  end;

procedure TCat.Hunt;
begin
  Writeln('I hunt');
end;

procedure TBird.Fly;
begin
  Writeln('I fly');
end;
        
class function TAnimalFactory.GetAnimal(aType: TAnimalType): IAnimal;
begin
  case aType of
    atCat: Result := TCat.Create;
    atBird: Result := TBird.Create;
  end;
end;

var
  i: ICat;
begin
    i := TAnimalFactory.GetAnimal(atCat);
    // [dcc32 Error] Project1.dpr(63): E2010 Incompatible types: 'ICat' and 'IAnimal'

    i.Hunt;
end.
2 Answers

In modern Delphi versions you can use generics, like this:

interface
uses
  System.Classes,
  System.Generics.Collections;

  ICat = interface(IAnimal)
    ['{15E79A9B-CF33-4672-8892-FCBC7A778C57}']  // Ctrl+Shift+G to generate GUID
    procedure Hunt;
  end;

  IBird = interface(IAnimal)
    ['{C9318161-2827-4D8C-AE0F-4D7B9A686F60}']
    procedure Fly;
  end;

  TAnimalFactory = class
    class function GetAnimal<Intf: IAnimal>: Intf;
  end;

implementation
uses
  System.SysUtils,
  System.TypInfo;
  { TAnimalFactory }

class function TAnimalFactory.GetAnimal<Intf>: Intf;
var
  G: TGUID;
  tmp: IInterface;
begin
  G := GetTypeData(TypeInfo(Intf))^.Guid;

  if G = ICat then
    tmp:=TCat.Create;
  if G = IBird then
    tmp:=TBird.Create;

  if not Supports(tmp, G, Result) then
    Result:=nil;
end;

var
  i: ICat;
begin
  i := TAnimalFactory.GetAnimal<ICat>;
  if Assigned(i) then  // if you not sure about interface support
    i.Hunt;
end;

I created the code I think you'll like. It is what you have done, with the fixe suggested by Zed and Bosshoss in the comments. I made a VCL application and also added a method in the base interface.

unit Unit7;

interface

uses
  System.SysUtils, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  IAnimal = interface
    procedure WhatILike;
  end;

  ICat = interface(IAnimal)
    procedure Hunt;
  end;

  IBird = interface(IAnimal)
    procedure Fly;
  end;

  TCat = class(TInterfacedObject, IAnimal, ICat)
    procedure WhatILike;
    procedure Hunt;
  end;

  TBird = class(TInterfacedObject, IAnimal, IBird)
    procedure WhatILike;
    procedure Fly;
  end;

  TAnimalType = (atCat, atBird);

  TAnimalFactory = class
    class function GetAnimal(aType: TAnimalType): IAnimal;
  end;

  TForm7 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  end;

var
  Form7: TForm7;

implementation

{$R *.dfm}

procedure TCat.Hunt;
begin
  ShowMessage('I hunt');
end;

procedure TBird.Fly;
begin
  ShowMessage('I fly');
end;

class function TAnimalFactory.GetAnimal(aType: TAnimalType): IAnimal;
begin
  case aType of
    atCat  : Result := IAnimal(TCat.Create);
    atBird : Result := IAnimal(TBird.Create);
    else     Result := nil;
  end;
end;

procedure TForm7.Button1Click(Sender: TObject);
var
    ABird : IBird;
    ACat  : ICat;
begin
    ACat  := ICat(TAnimalFactory.GetAnimal(atCat));
    ABird := IBird(TAnimalFactory.GetAnimal(atBird));

    ACat.Hunt;
    ABird.Fly;
    ACat.WhatILike;
    ABird.WhatILike;
end;


procedure TCat.WhatILike;
begin
    ShowMessage('I like to hunt');
end;

procedure TBird.WhatILike;
begin
    ShowMessage('I like to fly');
end;

end.
Related