pio pio
pio pio

Reputation: 794

Passing classes to interfaces

I have tested my own class with the dependency injection and now I have to implement it into production. The following is an excerpt of my class and relevant interface:

  ITableDB = interface
    ['{171DE959-8604-4CD3-ACEA-ACCE15E95621}']
    procedure Close;
    procedure Open;
    ...
  end;

  TNewStrategy=class(TObject)
  private
    FTableDB: ITableDB
    .....   
  public
    constructor Create (ATableDB: ITableDB....)
  end;

Instead of mocks and stubs I have to provide the class the real objects now. These are a number of third part components I have placed in a form at design time. Here one example:

type
  TForm1 = class(TForm)
    ThirdyPartDBTable1: ThirdyPartDBTable;
    NewStrategy: TNewStrategy;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

How can I pass ThirdyPartDBTable1 to TNewStrategy.Create ? I tried the following code:

  TMyThirdyPartDBTable = class(ThirdyPartDBTable, IITableDB)
  public
    procedure Close;
    procedure Open;
    ...
  end;

But when I try to change ThirdyPartDBTable1: ThirdyPartDBTable into ThirdyPartDBTable1: TMYhirdyPartDBTable; the compiler changes the reference TMYhirdyPartDBTable back to ThirdyPartDBTable.

Upvotes: 1

Views: 164

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 595329

In the code you showed, TNewStrategy is not derived from TComponent, so it cannot be placed on a TForm at design-time. You would have to create it at run-time, in which case you have access to its constructor and can pass ThirdyPartDBTable1 to it, eg:

procedure TForm1.FormCreate(Sender: TObject);
begin
  NewStrategy := TNewStrategy.Create(ThirdyPartDBTable1);
end;

However, if TNewStrategy were a TComponent descendant available at design-time, you could link ThirdyPartDBTable1 to NewStrategy at design-time if you change TNewStrategy to expose an ITableDB property instead of passing it in the constructor, eg:

TNewStrategy = class(TComponent)
private
  FTableDB: ITableDB
  .....   
public
  constructor Create(AOwner: TComponent); override;
published
  property TableDB: ITableDB read FTableDB write FTableDB;
end;

As long as ThirdyPartDBTable implements ITableDB then the Object Inspector and DFM streaming will allow it.

Update: since ThirdPartyDBTable does not implement ITableDB, you can use an interceptor class to implement it, eg:

interface

uses
  ..., ThirdPartyUnit;

type
  ThirdyPartDBTable = class(ThirdPartyUnit.ThirdyPartDBTable, ITableDB)
  public
    procedure Close;
    procedure Open;
  end;

  TForm1 = class(TForm)
    ThirdyPartDBTable1: ThirdyPartDBTable;
    NewStrategy: TNewStrategy;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

implementation

procedure ThirdyPartDBTable.Close;
begin
  ...
end;

procedure ThirdyPartDBTable.Open;
begin
  ...
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  NewStrategy := TNewStrategy.Create(ThirdyPartDBTable1 as ITableDB);
end;

end.

Upvotes: 2

Sertac Akyuz
Sertac Akyuz

Reputation: 54772

You cannot change the class of a component that you've put at design time by modifying it in the form declaration, the IDE owns the declarations in the upper public part of the form.

You can create your derived component at run time instead, or install it in a run time package and register with the component library. For a single time job, or for testing purposes, you can use an interposer class. In the below example I used a TPanel since I don't have any ThirdyPartDBTable, so be sure to put a panel on the test form. Also omitted the 'Close' method for brevity.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  ITableDB = interface
    ['{171DE959-8604-4CD3-ACEA-ACCE15E95621}']
    procedure Open;
  end;

  TPanel = class(extctrls.TPanel, ITableDB)
  public
    procedure Open;
  end;

  TNewStrategy=class(TObject)
  private
    FTableDB: ITableDB;
  public
    constructor Create(ATableDB: ITableDB);
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Panel1: TPanel;
    procedure Button1Click(Sender: TObject);
  private
    NewStrategy: TNewStrategy;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TPanel }

procedure TPanel.Open;
begin
  ShowMessage('Open what?');
end;

{ TNewStrategy }

constructor TNewStrategy.Create(ATableDB: ITableDB);
begin
  FTableDB := ATableDB;
end;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
begin
  NewStrategy := TNewStrategy.Create(Panel1 as ITableDB);
  NewStrategy.FTableDB.Open;
end;

end.

Upvotes: 0

Related Questions