在Delphi中使用接口的优缺点是什么?

时间:2011-02-01 10:12:52

标签: delphi interface

我已经使用了Delphi类一段时间但从未真正使用过接口。我已经读过一些关于它们的内容,但想了解更多。

我想听听您在Delphi中使用接口时遇到的有关编码,性能,可维护性,代码清晰度,层分离以及您可以想到的任何方面的优缺点。

谢谢和最好的问候

9 个答案:

答案 0 :(得分:26)

我现在能想到的只有:

优点:

  • 明确界面与实施之间的分离
  • 减少单位依赖性
  • 多重继承
  • 引用计数(如果需要,可以禁用)

缺点:

  • 类和接口引用不能混合(至少与引用计数一致)
  • 所有属性都需要Getter和setter函数
  • 引用计数不适用于循环引用
  • 调试困难(感谢gabr和Warren指出这一点)

答案 1 :(得分:12)

为答案添加更多优势:

  1. 使用接口来表示行为,行为的每个实现都将实现接口。
  2. API发布:发布API时,接口非常适合使用。您可以在不给出实际实现的情况下发布接口。因此,您可以自由地进行内部结构更改,而不会给客户带来任何问题。

答案 2 :(得分:10)

我所说的是,在我的delphi的愿望清单中,没有引用计数的接口非常高!

- >接口的实际用途是接口的声明。不是引用计数的能力!

答案 3 :(得分:7)

接口有一些SUBTLE缺点,我不知道人们在使用它们时会考虑:

  1. 调试变得更加困难。我在调试器中看到了很多进入接口方法调用的奇怪困难。

  2. Delphi中的接口带有IUnknown语义,如果你喜欢或不喜欢它,你坚持引用计数是一个受支持的接口。因此,对于在Delphi世界中创建的任何接口,您必须确保正确处理引用计数,如果不这样做,则最终会出现泄漏。当你想避免引用计数时,你唯一的选择是覆盖addref / decref并且实际上不释放任何东西,但这并非没有它自己的问题。我发现负载较多的接口代码库有一些最难以发现的访问冲突和内存泄漏,我认为这是因为将refcount语义和默认的delphi语义(所有者)结合起来非常困难。释放物品,没有其他人这样做,大多数物品都是在父母的整个生命中生活。)。

  3. 使用Interfaces的糟糕实现可能会产生一些讨厌的代码气味。例如,在定义类的初始具体实现的同一单元中定义的接口,添加接口的所有权重,而不真正提供接口的用户和实现者之间的适当分离。我知道这不是接口本身的问题,而是对编写基于接口的代码的人更多的狡辩。请将您的接口声明放在仅包含这些接口声明的单元中,并避免因将接口声明重命名为与实现者类相同的单元而导致的单元到单元依赖性地狱。

答案 4 :(得分:5)

当我希望具有不同祖先的对象提供公共服务时,我主要使用接口。根据我自己的经验,我能想到的最好的例子是名为IClipboard的接口:

IClipboard = interface
  function CopyAvailable: Boolean;
  function PasteAvailable(const Value: string): Boolean;
  function CutAvailable: Boolean;
  function SelectAllAvailable: Boolean;
  procedure Copy;
  procedure Paste(const Value: string);
  procedure Cut;
  procedure SelectAll;
end;

我有一堆从标准VCL控件派生的自定义控件。他们每个都实现这个接口。当剪贴板操作到达我的某个表单时,它会查看活动控件是否支持此接口,如果是,则调度相应的方法。

对于一个非常简单的接口,您可以使用of object事件处理程序执行此操作,但一旦它变得足够复杂,接口就可以正常工作。事实上,我认为这是一个非常好的模拟。使用一个of object事件不适合该功能的界面。

答案 5 :(得分:4)

接口解决了某种问题。主要功能是......好吧,...定义接口。区分定义和实现。

如果要指定或检查类是否支持一组方法 - 请使用接口。

你不能以任何其他方式做到这一点。

(如果所有类都继承自相同的基类,那么抽象类将定义接口。但是当您处理不同的类层次结构时,您需要接口来定义您共有的方法...)

答案 6 :(得分:3)

额外注意事项 缺点:表现

我认为很多人太过于轻率地忽视接口的性能损失。 (不是我不喜欢和使用接口,但你应该知道你正在进入什么)。接口可能很昂贵,不仅仅是_AddRef / _Release命中(即使你只是返回-1),而且属性也需要有一个Get方法。根据我的经验,类中的大多数属性都可以直接访问读取访问器(例如,属性Prop1:整数读取FProp1写入SetProp1)。更改直接,不会对函数调用进行惩罚访问会对您的速度产生重大影响(特别是当您开始在循环内添加10个属性调用时。

例如,使用类

的简单循环
for i := 0 to 99 do
begin
  j := (MyClass.Prop1 + MyClass.Prop2 + MyClass.Prop3) / MyClass.Prop4;
  MyClass.Update;
  // do something with j
end;
当类成为接口时,

从0个函数调用转到400个函数调用。在该循环中添加更多属性,它会很快变得更糟。

你可以通过一些提示来改善_AddRef / _Release惩罚(我相信还有其他一些提示。这不在我的脑海中):

  • 使用WITH或赋值给temp变量只会产生每个代码块一个_AddRef / _Release的惩罚
  • 始终使用 const 关键字将接口传递给函数(否则,每次调用该函数时都会发生额外的_AddRef / _Release。

答案 7 :(得分:1)

我们 使用接口(除了COM / ActiveX之外)的唯一情况是,当我们需要多继承时,接口是获取它的唯一方法。在我们尝试使用接口的其他几种情况下,我们遇到了各种各样的问题,主要是引用计数(当对象作为类实例和通过接口访问时)。

所以我的建议是只有当你知道你需要时才使用它们,而不是当你认为它可以让你的生活在某些方面更容易。< / p>

更新:正如大卫提醒的那样,通过接口,您只能获得多个接口继承,而不是实现。但这对我们的需求来说很好。

答案 8 :(得分:1)

除了其他已经列出的内容之外,接口的一个重要功能是聚合它们的能力。

我刚才写了一篇关于该主题的博客文章,可以在这里找到:http://www.nexusdb.com/support/index.php?q=intf-aggregation(tl;博士:你可以有多个对象,每个对象实现一个接口,然后将它们组装成一个汇总到外面世界看起来像是实现所有这些接口的单个​​对象)

您可能还想看看那里链接的“界面基础”和“高级界面使用和模式”帖子。