我已经使用Delphi类有一段时间了,但从未真正开始使用接口。我已经读了一些关于它们的内容,但想学习更多。
我想听听您在使用Delphi中接口时遇到的优缺点,包括编码、性能、可维护性、代码清晰度、层次分离以及您可以想到的任何方面。
目前我能想到的有:
优点:
缺点:
除了上面的回答之外,还有以下几个优点:
我想说的是,没有引用计数的接口非常高在我的Delphi愿望清单上!
--> 接口的真正用途是声明一个接口,而不是具备引用计数的能力!
使用接口也存在一些微妙的缺点,可能在使用时人们并没有考虑到:
调试变得更加困难。我曾经见过很多在调试器中调用接口方法时出现的奇怪问题。
Delphi中的接口带有IUnknown语义,无论你喜欢与否,你都必须处理好引用计数,并且如果处理不当,会导致内存泄漏。当你想要避免引用计数时,唯一的选择是重写addref/decref,但这也不是没有问题的。我发现那些使用接口较多的代码库中,最难找到访问冲突和内存泄漏,我认为这是因为很难将引用计数语义和默认的Delphi语义(所有者释放对象,其他人不释放,大多数对象的生命周期与其父对象相同)结合起来。
糟糕的接口实现可能会导致一些令人讨厌的代码气味。例如,在定义类的初始具体实现的同一单元中定义接口,增加了接口的负担,却没有真正提供用户和实现者之间的正确分离。我知道这不是接口本身的问题,而更多地是对那些编写基于接口的代码的人的挑剔。请将您的接口声明放在只包含这些接口声明的单元中,并避免将接口声明与实现类混合在同一个单元中,导致单元之间依赖关系混乱。
当我需要不同祖先的对象提供共同服务时,我通常使用接口。我自己经历中最好的例子是一个名为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;
of object
事件处理程序来完成,但一旦变得足够复杂,使用接口效果很好。事实上,我认为这是一个很好的比喻。在需要单个 of object
事件无法适用功能的情况下,请使用接口。接口解决了某些问题。其主要功能是定义接口,区分定义和实现。
当您想要指定或检查类是否支持一组方法时,请使用接口。
您不能以其他方式完成此操作。
(如果所有类都继承自相同的基类,则抽象类将定义接口。但是,当您处理不同的类层次结构时,需要接口来定义它们共有的方法...)
GetCount()
方法。它们不必是相同的方法。这是无法通过RTTI实现的。 - mghie关于缺点的额外说明:性能
我认为许多人对接口的性能惩罚过于轻率地忽视了。(并不是说我不喜欢和使用接口,但你应该意识到自己所涉及的内容)。接口可能会很昂贵,不仅因为_AddRef / _Release 的影响(即使你只返回-1),而且属性必须具有Get方法。根据我的经验,类中的大多数属性都有直接访问的读取器(例如,propery Prop1:Integer read FProp1 write 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 惩罚(我相信还有其他技巧。这是我随意想到的):
j := MyClass.CalculateJ;
替换j := ...
。提示:如果分析识别出通过接口重复调用导致性能问题,请考虑重构以将责任移动到更合适的位置。 - Disillusioned除了 COM/ActiveX 等情况下,我们唯一需要使用接口的情况是当我们需要多重继承时,而接口是唯一的实现方式。在其他几种情况下,当我们尝试使用接口时,我们遇到了各种问题,主要是引用计数方面的问题(当对象既作为类实例又通过接口访问时)。
因此,我的建议是只有在您知道您需要它们时才使用它们,而不是认为它们可以在某些方面使您的生活更轻松。
更新:正如 David 提醒的那样,使用接口只能获得接口的多重继承,而不能获得实现的多重继承。但对于我们的需求来说,这是可以接受的。
除了其他人已经列出的内容之外,接口的一个重要优点是能够聚合它们。
我之前写过一篇关于这个主题的博客文章,可以在这里找到:http://www.nexusdb.com/support/index.php?q=intf-aggregation(简而言之:您可以拥有多个对象,每个对象都实现一个接口,然后将它们组装成一个聚合体,对外界来说,它看起来像是一个实现了所有这些接口的单个对象)
您还可以查看链接的“接口基础”和“高级接口使用和模式”文章。
TInterfacedObject
继承,而是自己实现IInterface
中的方法,并始终将引用计数返回 -1。如果需要更多细节,请提出单独的问题。 - jpfollenius