抽象与接口 - 在Delphi中分离定义和实现

时间:2010-02-17 07:52:31

标签: delphi oop interface abstract-base-class

使用接口或抽象类分离定义和实现的更好方法是什么?

实际上,我不喜欢将引用计数对象与其他对象混合。我想在保持大项目时这可能成为一场噩梦。

但有时我需要从2个或更多类/接口派生一个类。

你的经历是什么?

6 个答案:

答案 0 :(得分:24)

答案 1 :(得分:8)

我怀疑这是一个“更好的方法”的问题 - 他们只是有不同的用例

  • 如果您没有类层次结构,并且您不想构建一个层次结构,并且将不相关的类强制到同一层次结构中甚至没有意义 - 但是你想要平等对待一些类,而不必知道类的具体名称 - >

    接口是可行的方法(例如,考虑Javas 可比较 Iterateable ,如果您必须从这些类派生(假设它们是class =) ,他们将毫无用处。

  • 如果具有合理的类层次结构,则可以使用抽象类为此层次结构的所有类提供统一的访问点,并且您甚至可以实现默认行为等。< / LI>

答案 2 :(得分:5)

您可以使用没有引用计数的接口。编译器为所有接口添加对AddRef和Release的调用,但这些对象的生命周期管理方面完全取决于IUnknown的实现。

如果从TInterfacedObject派生,对象生存期确实会被引用计数,但是如果从TObject派生自己的类并实现IUnknown而不实际计算引用并且在Release的实现中没有释放“self”那么你将得到一个基础支持接口但具有正常管理生命周期的类。

由于自动生成的对编译器注入的AddRef()和Release()的调用,你仍然需要小心那些接口引用,但这与注意常规TObject的“悬空引用”并没有多大区别。

这是我过去在复杂和大型项目中成功使用的东西,甚至包括支持接口的ref计数和非ref计数对象。

答案 3 :(得分:3)

在Delphi中,有三种方法可以将定义与实现分开。

  1. 您可以在每个单元中分隔,您可以将publuc类放在接口部分中,并在实现部分中实现它。代码仍然驻留在同一个单元中,但至少代码的“用户”只需要读取接口而不是实现的内容。

  2. 在类中使用虚拟或动态声明的函数时,可以覆盖它们 子类。这是大多数类库使用的方式。看看TStream及它的派生类,如THandleStream,TFileStream等。

  3. 当您需要不同于层次派生的层次结构时,可以使用接口。接口总是从IInterface派生而来,它被建模为基于COM的IUnknown:你可以获得引用计数和查询类型信息。

  4. 3: - 如果从TInterfacedObject派生,引用计数确实会处理对象的生命周期,但这不是必须的。 - 例如,TComponent也实现了IInterface但没有引用计数。这带有一个大警告:确保在销毁对象之前将接口引用设置为nil。编译器仍然会向你的界面插入递减调用,看起来仍然有效但不是。第二:人们不会期待这种行为。

    在2和3之间选择有时是非常主观的。我倾向于使用以下内容:

    • 如果可能,请使用virtual和Dynamic并覆盖派生类中的那些。
    • 使用interfaces时:创建一个基类,接受对interfcae实例的引用作为变量,并使接口尽可能简单;对于每个方面尝试创建一个单独的intercae变量。在没有指定接口时尝试使用默认实现。
    • 如果上述限制太多:开始使用TInterfacedObject-s并真正注意可能的周期,从而导致内存泄漏。

答案 4 :(得分:1)

根据我对超大型项目的经验,这两种模型不仅运行良好,甚至可以毫无问题地共存。接口具有优于类继承的优点,因为您可以将特定接口添加到不会从共同祖先下降的多个类,或者至少在没有将代码引入到层次结构中的情况下,您可能会冒险在代码中引入新错误已被证明有效。

答案 5 :(得分:1)

我不喜欢COM接口,以至于从来没有使用它们,除非有其他人生产过它。也许这来自我对COM和类型库的不信任。我甚至将“伪造”接口作为具有回调插件的类而不是使用接口。我想知道是否有其他人感受到我的痛苦,并避免使用界面,好像他们是瘟疫?

我知道有些人会认为我避免接口是一个弱点。但我认为所有使用Interfaces的Delphi代码都有一种“代码味道”。

我喜欢使用委托和任何其他机制,将我的代码分成几个部分,并尝试用类做我能做的一切,从不使用接口。我不是说这很好,我只是说我有我的理由,而且我有一个规则(有时可能是错的,有些人总是错的):我避免接口。