0

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

Johan
  • 74,508
  • 24
  • 191
  • 319
Luiz Alves
  • 2,575
  • 4
  • 34
  • 75
  • I don't understand why you're declaring identical interfaces in multiple units. Declare it once in a common unit, and then use that unit throughout your program. – Rob Kennedy Apr 29 '16 at 18:46
  • Because, it´s not mine. It´s from other developer. He changes the version number at each new version, and other small things. But, what I need to use from that units continues the same, except version number and some function result values . But I always will need decide what version to use at runtime. – Luiz Alves Apr 29 '16 at 19:05

1 Answers1

3

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;
Johan
  • 74,508
  • 24
  • 191
  • 319
  • Yes, I know. This is what I am doing now. But I have many procedures and functions using "a" variable. I'd have to duplicate many lines of code only to change from "a" variable to "b" variable. And I need accces both units depending on condition. I´d like to have only one variable to access the interfaces, because they are similar, only changing your versions and other no important things. – Luiz Alves Apr 29 '16 at 17:57
  • Ok, but I need to choice the unit at runtime, no in compilation time – Luiz Alves Apr 29 '16 at 18:04
  • Phuffff, now I've covered everything. I got tired of typing out the interface and method names, so I got a bit creative at the end. – Johan Apr 29 '16 at 18:12
  • Thank you Mr Johan, but I need to choice the unit at runtime. – Luiz Alves Apr 29 '16 at 18:18
  • That's not possible, you can only do that if you use dll's. There is no dynamic loading for units. – Johan Apr 29 '16 at 18:20
  • Ok, Thank you very much – Luiz Alves Apr 29 '16 at 18:21
  • Why does that last paragraph in the answer not work for you? – Johan Apr 29 '16 at 18:22
  • Because, I have cases that I need to choice at runtime depend on condition. For sample, if I have a person with age < 20 I need to use tissV01 otherwise I need tissV02. I only can decide at runtime. – Luiz Alves Apr 29 '16 at 19:00
  • So fine, than the last paragraph would work. If you want you can externalize the selection of the interface to a separate unit/function. `unit choosex; interface function InterfaceXorY(params): ICommonInterface;` – Johan Apr 29 '16 at 19:04
  • Sorry, but can you elaborate it? – Luiz Alves Apr 29 '16 at 19:06
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/110669/discussion-between-johan-and-luiz-alves). – Johan Apr 29 '16 at 19:09