Luiz Alves
Luiz Alves

Reputation: 2645

Acessing several units implementing a Interface with equal names

I have two or more units I need download from third party when your versions have changes.

I use xml databind to generate the units. They are something as:

unit tissV01;

interface

uses .....;

type
  IXMLMensagemTISS = interface(IXMLNode)
    ['{11773827-F0A1-42E0-99E1-E221DFAF8542}']
    { Property Accessors }
  end;


function GetmensagemTISS(Doc: IXMLDocument): IXMLMensagemTISS;

implementation

function GetmensagemTISS(Doc: IXMLDocument): IXMLMensagemTISS;
begin
  Result := XXXX as IXMLMensagemTISS;
end;

end.

Unit tissV02

unit tissV02;

interface

uses .....;

type
  { IXMLMensagemTISS }
  IXMLMensagemTISS = interface(IXMLNode)
    ['{11773827-F0A1-42E0-99E1-E221DFAF8542}']
    { Property Accessors }
    property Cabecalho: string read Get_Cabecalho;
  end;

function GetmensagemTISS(Doc: IXMLDocument): IXMLMensagemTISS;

implementation

function GetmensagemTISS(Doc: IXMLDocument): IXMLMensagemTISS;
begin
  Result := XXXX as IXMLMensagemTISS;
end;

end.

In my app I need to have a choice what unit I have to use:

unit test;

interface

uses tissV01,tissV02, .......;

type 
  TMyform = class(TForm)
  public
    msg3:IXMLMensagemTISS;   
  end;

implementation

procedure TMyform.ExecuteMessage:
var 
  xmlTISS : TXmlDocument;
begin
  xmlTISS := TXmlDocument.Create(nil); 
  if condition  then
    msg3 := tissV01.GetmensagemTISS(xmlTISS)
  else msg3 := tissV02.GetmensagemTISS(xmlTISS);
  with msg3.Cabecalho do  something;
end; 

end.

Logically, it doesn´t work because IXMLMensagemTISS is common to both units.

Is there some workaround to do it without I have to change the name of the Interface names(IXMLMensagemTISS)?

I´d like to simplify my code and I need maintain many units of this type in the future. The problem is that all implement IXMLMensagemTISS and I can do nothing to change it.

I´d not like to create many msg variables such as msgV01:=tissv01.GetmensagemTISS, msgV01:=tissv02.GetmensagemTISS, ... and so on

Upvotes: 0

Views: 100

Answers (1)

Johan
Johan

Reputation: 76537

If you have two identical identifiers in different units you can prefix the unit name to differentiate them.

var
  a: tissV01.IXMLMensagemTISS;
  b: tissV02.IXMLMensagemTISS;

In your sample code however you need to make an explicit choice which interface to use.

uses
  tissV01, tissV02;  //last unit in uses clause gets priority.

type 
  TMyform = class(TForm)
  public
    msg3: tissV01.IXMLMensagemTISS;   //allowed
    msg2: tissV02.IXMLMensagemTISS;   //allowed
    msgx: IXMLMensagemTISS; //ambigous, will evaluate to tissV02.IXMLMensagemTISS;
  end;

The last unit in a uses clause gets prioritised.
This fact is often abused to override built in classes and interfaces with custom ones.

If you wish to delay the choice based on some condition you can use conditional compilation.
Either in the uses clause (making use of the priority effect of the uses clause order),

unit DoWork;

interface

uses
 {$ifdef V01HasPriority}
 tissV02, tissV01;
 {$else}
 tissV01, tissV02;
 {$endif}

or explicitly in the declarations

var
  a: {$ifdef useV01} tissV01.IInt {$else} tissV02.IInt {$endif}

You then make the choice somewhere else using a {$define V01HasPriority} that compiles prior to the {$ifdef ...}.
You can also declare the {$define ...} in the IDE.

Project > Options > Delphi Compiler > Conditional defines.

You can only choose the interface at runtime if the interfaces are compatible.
That means the interfaces inherit from a common ancestor.
Every interface has a common ancestor in IInterface, however it is best to choose an interface as close as possible to both.

Then you declare a variable as that common ancestor:

var
  a: ICommonInterface;
begin
  if x=1 then a:= tissV01.NewXMLInterface
  else a:= tissV02.NewXMLInterface;
  if Supports(a, tissV01.IXMLInt) then tissV01.IXMLInt(a).DoV01Things
  else tissV02.IXMLInt(a).DoV02Things;  

If both interfaces have the same signature then things are much easier (and much saner).

var
  a: IXMLCommon;
begin
  if x=1 then a:= tissV01.NewXMLInterface
  else a:= tissV02.NewXMLInterface;
  a.DoCommonThings(param1, param2);

Centralizing the decision making
Of course if you have lots of decisions to make it's (sometimes) better to centralize them then to spread them all over your program.

So why not create a unit where all the decision making gets done, like so:

unit IvoryTower;

interface

function InterfaceXorY(const person: TPerson): ICommonIntf;

implementation

function InterfaceXorY(const person: TPerson): ICommonIntf;
var
  WhatToDo: TSomething;
begin
  WhatToDo:= DatabaseY.TableX.GetData(Person);
  case WhatToDo of
    XYZolog: Result:= Unit1.I1;
    Galaga: Result:= Unit2.I2;
    Twinbee: Result:= Unit3.I4;
    else Assert(false, 'what to do outside of valid range');
  end; {case}
end;

Upvotes: 3

Related Questions