访问多个实现同名接口的单元

Acessing several units implementing a Interface with equal names

我有两个或多个单元需要从第三方下载,当你们的版本有变化时。

我使用 xml 数据绑定来生成单位。它们是这样的:

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.

单位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.

在我的应用程序中,我需要选择我必须使用的单位:

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.

从逻辑上讲,它不起作用,因为 IXMLMensagemTISS 对两个单元都是通用的。

是否有一些解决方法可以做到这一点而不必更改接口名称 (IXMLMensagemTISS)?

我想简化我的代码,并且我需要在未来维护很多这种类型的单元。问题是所有人都实现了 IXMLMensagemTISS 而我无法改变它。

我不想创建很多 msg 变量,例如 msgV01:=tissv01.GetmensagemTISS、msgV01:=tissv02.GetmensagemTISS、...等等

如果您在不同的单位中有两个相同的标识符,您可以在单位名称前加上前缀以区分它们。

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

然而,在您的示例代码中,您需要明确选择要使用的接口。

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;

uses 子句中的最后一个单元被优先考虑。
这个事实经常被滥用来覆盖内置 类 和与自定义接口的接口。

如果您希望根据某些条件延迟选择,您可以使用条件编译。
或者在uses子句中(利用uses子句顺序的优先效应),

unit DoWork;

interface

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

或明确在声明中

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

然后您可以使用在 {$ifdef ...}.
之前编译的 {$define V01HasPriority} 在其他地方做出选择 您还可以在 IDE 中声明 {$define ...}

Project > Options > Delphi Compiler > Conditional defines

如果接口兼容,您只能在运行时选择接口。
这意味着接口继承自一个共同的祖先。
每个接口在 IInterface 中都有一个共同的祖先,但是最好选择一个尽可能接近两者的接口。

然后你声明一个变量作为那个共同的祖先:

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;  

如果两个接口具有相同的签名,那么事情就容易多了(也更理智)。

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

集中决策
当然,如果你有很多决定要做,那么(有时)最好将它们集中起来,然后将它们分散到你的程序中。

那么为什么不创建一个可以完成所有决策的单元,就像这样:

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;