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
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;