【问题标题】:COM: If I change the parent of an Interface do I need to create a new Interface?COM:如果我更改接口的父级,我需要创建一个新接口吗?
【发布时间】:2013-07-03 11:52:31
【问题描述】:

我一直在向 COM 接口添加新功能,从我所读到的唯一方法(并保持向后兼容)是创建一个继承旧接口并添加新方法的新接口。我试过这样做,但我的层次结构已经很复杂了。

说这就是我目前的界面:

基础接口 | 衍生接口

我想为基础接口添加一些功能,现在看起来像这样:

IBaseOld接口 | 基础接口 | 衍生接口

因为我已经添加了这个新功能,我需要创建一个新的 IDerivedInterface 吗?我已经按照目前的方式进行了尝试,但它似乎不适用于所有情况。

我真的想要一些确认,如果可能的话,解释一下为什么

我需要进一步解释我的理想情况是什么以及为什么:

我想向 IBaseInterface 类添加一些方法,以便所有派生类都可以使用。我认为可能是这样的:

IBaseOld接口 / \ IDerivedOldInterface IBaseNewInterface \ / IDerivedNew接口

我知道应该避免 Diamond 继承,但它们只是接口,所以我认为没关系。

当用户请求 INewDerivedInterface 之一时,具有这种新结构。查询接口将返回该 IID 的正确接口,无论是 INewDerivedInterface 还是 IOldDerivedInterface。

【问题讨论】:

  • 更改接口的 IID 至关重要。因此,您不会有对更改的界面一无所知的旧代码尝试使用新服务器。并且不要忘记一个 coclass 可以实现任意数量的接口,它们不必相互继承。
  • 我已经添加了关于我认为需要什么的进一步解释。所有标有“旧”的接口仍然具有与以前相同的 IID,只有新接口生成了新的 IID

标签: c++ com c++builder


【解决方案1】:

那行不通。正如您所说,为了保持兼容性,您不能修改已发布的接口。但是通过更改继承,您正在修改接口。

因此,您可以将新方法添加到新子类中,然后您将拥有以下结构:

基础接口 | 衍生接口 | IDerivedInterface2

或者你应该做的是添加一个实现对象支持的新接口。因此,您只需声明您的实现类支持多个接口,而不是使用继承。

在这种情况下,您将不理会现有的继承结构,并拥有如下内容:

IBaseInterface INewInterface | 衍生接口

然后像这样实现类:

class TMyObject: public TInterfacedObject, IDerivedInterface, INewInterface

【讨论】:

    【解决方案2】:

    您可以通过三种方式向IBaseInterfaceIDerivedInterface 接口添加新功能:

    1. 您将新方法添加到IDerivedInterface 的底部。知道这些新方法的新建应用程序将能够使用它们。早期构建的应用程序将安全地使用旧方法。您不能将方法添加到 IBaseInterface 或从另一个接口重新继承它,因为这会转移相关方法。 从您最初问题的上下文来看,我感觉您正在更改一系列外部软件项目使用的公共接口,并且您不希望您的更改导致任何问题。如果是这样,您可能会想要限制走这条路,即使它看起来很简单并且可以完成工作(请参阅下面的原因)。

    2. 您可以从IBaseInterfaceIDerivedInterface 派生新接口,新建的应用程序将使用它。请注意必须为新接口分配新的 IID。

    3. 您定义了新接口(例如上面@DavidHeffernan 中的INewInterface)并且您的COM 对象实现了更多接口。新建的应用可以查询新接口并调用其方法。

    上面的第 3 项可能是扩展对象的典型、简单和方便的方法。仅当您需要将方法保留在“主”界面上时,第 2 项才有意义,例如对于旧版客户端,例如 VB6。第 1 项是一种无需过多修改代码即可快速添加内容的方法,它也确实有效 - 唯一的问题是客户端无法安全地检测服务器是仅实现旧方法还是新方法。

    【讨论】:

    • 我真的不喜欢你的选项 1。我希望你没有说出来,我不能在它还在的时候 +1,即使其他点都很好。跨度>
    • @RogerRowland:如果这个接口是内部的,比如不对外发布,这是最简单的方法。每次要添加新方法时,您实际上并不需要添加新接口。最后一段解释了问题。
    • 是的,我知道你从哪里来,但如果界面尚未公开,那么所有的赌注都将关闭,OP 可以重构整个界面。
    • @RogerRowland:这就是我写的原因:3 是典型的,2 可能出于其他原因需要,1 很棘手,甚至可能不适用。这仍然不会使这种可扩展性方法变得不切实际。
    • 不,这些后续的 cmets 澄清了这一点。我只是觉得你的回答不够明显,我想我希望你能稍微编辑一下,这样我就可以投票了——也许我太迂腐了 ;-)
    【解决方案3】:

    向基接口添加方法(或将其替换为扩展它的另一个方法)会更改所有派生接口的 vtable 布局,因此您确实需要使用新的 IID 创建新的派生接口。

    【讨论】:

    • 谢谢,我想这解决了。不过,看起来确实需要做很多工作。我试图在一个已经有至少 100 个接口的系统中实现这一点,这些接口已经随着时间的推移而改变。到目前为止,还没有向前或向后兼容,并且不知何故它已经被忽视(或被忽略):)
    【解决方案4】:

    要向IBaseInterface 添加新功能,您需要创建一个新界面,但不要更改现有的层次结构。你可以做的是让实现IBaseInterface也实现新接口,例如:

    type
      IBaseInterface = interface
        ...
      end;
    
      IBaseInterface2 = interface(IBaseInterface)
        ...
      end;
    
      IDerivedInterface = interface(IBaseInterface)
        ...
      end;
    
      TBaseClass = class(TInterfacedObject, IBaseInterface, IBaseInterface2)
        ...
      end;
    
      TDerivedClass = class(TBaseClass, IDerivedInterface)
        ...
      end;
    

    这样,任何想要使用新的IBaseInterface2 功能的代码都可以查询该接口的任何IBaseInterface,如果成功则使用新功能,例如:

    var
      Base2: IBaseInterface2;
    begin
      if Supports(AnyBaseOrDerivedInterfacePtr, IBaseInterface2, Base2) then
      begin
        // use Base2 as needed...
      end;
    end;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-13
      • 1970-01-01
      • 2023-03-08
      • 1970-01-01
      相关资源
      最近更新 更多