【问题标题】:Does using interfaces forces to implement interfaces return types?使用接口是否强制实现接口返回类型?
【发布时间】:2011-10-21 12:00:23
【问题描述】:

使用接口时,实现类方法是强制返回接口,还是有办法返回对象?

举个例子来说明:

在单位 EmployeeIntf:

IEmployee = interface(IInterface)
  function CoWorker: IEmployee; // We dont know anything about TEmployee here
end;

单位员工:

uses EmployeeIntf;
...

TEmployee = class(TObject, IEmployee)
public
  function CoWorker: IEmployee; // Returns an error if set to TEmployee
end;

如果方法返回 TEmployee,则当前代码在编译器错误时停止:E2211 “CoWorker”声明与接口 IEmployee 中的声明不同(使用 Delphi 2010)

TEmployee.CoWorker 方法是强制返回一个接口,还是有办法返回一个 TEmployee,只要 TEmployee 是一个 IEmployee?

如果在这种情况下只允许接口,那么 OO 设计的原因是什么?

[编辑]

  • 正如许多贡献者所问的,我不需要对 TEmployee 进行 ref 计数,并且希望将上述问题与任何 ref 计数考虑隔离开来。

  • 这个问题的背景是需要在外部组件中(在单独的包中)使用 TEmployee 的一组非常有限的公共函数。由于uses部分中有太多不需要的依赖项,我不能简单地在包中导入'Employee'单元,所以我正在寻找一个松散耦合的解决方案。

谢谢,

【问题讨论】:

  • 从您的 cmets 到一些答案来看,您应该更新问题以提供这些 cmets 中给出的额外信息。
  • 大卫,谢谢你的建议,完成了。

标签: delphi interface


【解决方案1】:

您可以通过引入方法解析子句来使编译器满意:

type
  TEmployee = class(TInterfacedObject, IEmployee)
    function IEmployee.CoWorker = IEmployeeCoWorker;
  public
    function IEmployeeCoWorker: IEmployee;
    function CoWorker: TEmployee;
  end;

function TEmployee.IEmployeeCoWorker: IEmployee;
begin
  result := CoWorker;
end;

当然,这会使代码复杂化,但如果它确实是您所需要的......

【讨论】:

  • 经过测试和工作。感谢 Uwe 提供了问题的解决方案 - 周四,我怀疑问题的根本原因出在糟糕的设计领域(紧密耦合的类阻止外部组件在不重新导入整个项目的情况下使用)......跨度>
【解决方案2】:

实现类方法是否强制返回接口?

否,但实现必须与接口定义的签名相匹配。

有没有办法返回对象?

是的,如果您相应地声明接口。

type
  IEmployee = interface
    function CoWorker: TEmployee;
  end;

【讨论】:

  • 最后一点使整个界面无用。如果您将接口绑定到特定的实现,则没有理由首先使用该接口...
  • @Smasher 这意味着 INTA* 接口没有理由存在于Toolsapi 中,而且它们毫无用处。
  • TOndrej:我不知道这些接口,但是当同事是“具体”员工时,拥有一个“抽象”员工的意义何在? IEmployee 的任何其他实现几乎是不可能的。
  • @TOndrej:正如问题所言,IEmployee 对任何实现类一无所知。您的第一点也是我所期待的,但即使声明一个返回符合接口签名的对象的方法,编译器也不接受它。
  • @Smasher 是否有一点取决于上下文,从提供的信息中不清楚。我刚刚回答了问题。
【解决方案3】:

我假设您希望 CoWorker 函数返回 IEmployee,以使该接口的客户受益。

但是,实现代码有时有正当理由访问实现对象。当然,这样做确实会产生一种可能不受欢迎的耦合。

从 Delphi 2010 开始,您可以使用 as 运算符来访问实现对象。

var
  EmpIntf: IEmployee;
  EmpImp: TEmployee;
...
  EmpImp := EmpIntf as TEmployee;

请注意,您现在正在颠覆对象的生命周期管理。如果你在接口引用变为零之后访问EmpImp,那么你就有麻烦了。

最后,我要评论说,使用这种方法会产生不良设计的味道。通常最好找到一种方法,而不是我们 as 以这种方式。

【讨论】:

  • 我同意我的请求的核心原因肯定是糟糕的设计(或者至少,在 TEmployee 和使用的单元之间没有足够的松散耦合类)。肯定会问另一个关于 OO 设计方面的问题。
【解决方案4】:

Delphi 不支持covariant return types. 所以这与接口无关,只是语言本身的限制。

不过,通常这不是问题,因为客户通常应该使用IEmployee 并且不应该关心同事的具体类型。您必须提供更多详细信息(可能在新问题中)为什么需要特定类型。

如果你使用接口引用计数,你有更多的理由只使用接口类型引用,因为混合使用接口和对象引用会打乱引用计数。

【讨论】:

  • 在实际应用中,IEmployee只提供了非常有限的一组TEmployee公共函数(我不想将接口扩展到TEmployee的所有公共函数)。因此,返回一个比 IEmployee 更完整且符合接口要求的 TEmployee 似乎是合法的。我从您的观点理解这纯粹是技术限制,而不是 OO 不好的做法?
  • @user315561 在这种情况下你为什么要使用接口?
  • @David:此类与仅使用此类的少数公共方法的外部组件(在单独的包中)之间存在松散耦合。由于太多不需要的依赖项,我不能简单地导入包中的类单元。
  • @user 好的,我能理解。我真的敦促您扩展问题以包括 cmets 中提供的所有详细信息。
  • 标记为有用,因为 Delphi 并没有真正提供在接口方法需要接口时返回对象的方法(尽管 Uwe 提出了一种解决方法)
【解决方案5】:

如果要定义一个返回接口的接口函数,为什么要在实现中返回一个对象?

相信你只是不知道如何实现“函数CoWorker:IEmployee”。如果这是问题所在,这里有一个简单的解决方案:

TEmployee = class(TInterfacedObject, IEmployee)
public
  function CoWorker: IEmployee;
end;

function TEmployee.CoWorker: IEmployee;
begin
  Result := TEmployee.Create;
end;

顺便说一句,您应该从 TInterfacedObject 派生接口实现对象,而不是从 TObject。

[编辑]

至于你的澄清 - 你不能为 TEmployee 定义一个小的公共接口,然后从两个接口派生这个类吗?像这样:

type
  IEmployeePublic = ...
  IEmployee = ...

  TEmployee = class(TInterfacedObject, IEmployee, IEmployeePublic)
    function CoWorker: IEmployeePublic;
  end;

【讨论】:

  • 关于TInterfacedObject:仅当您希望对对象进行引用计数时才这样做。在这种情况下,请确保不要混合使用对象和接口引用。
  • +1 表示“你应该从 TInterfacedObject 派生接口实现对象”
  • 对于第 1 点,请参阅对 Smasher 的评论。关于引用计数,我需要没有引用计数的纯接口并在 TEmployee 中禁用它。对这个新环境有什么建议吗?
  • 好吧,即使在这种情况下,CoWorker 也会返回一个接口。根据当前的答案,除了痛苦的解决方法外,似乎根本不可能做到所描述的事情。无论如何谢谢:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-16
  • 1970-01-01
  • 1970-01-01
  • 2013-07-22
  • 2015-05-08
  • 2019-02-10
  • 1970-01-01
相关资源
最近更新 更多