您如何改进和版本化接口?


22

假设您有一个界面IFoo

public interface IFoo {
    void Bar(string s);
    int Quux(object o);
}

在API的第2版中,您需要Glarg向该接口添加方法。在不破坏现有API用户和保持向后兼容性的情况下,该如何做?这主要是针对.NET,但也可以应用于其他框架和语言。


您可以毫无问题地添加。当您更改/删除已经存在的东西时,问题就来了。
钻机2012年

1
@Rig:至少在C#中,如果将方法添加到接口而不将其添加到实现该接口的类中,则将出现编译错误。
Malice'3

好吧,那是真的。我从用户类方案中考虑的更多,与更改方法签名或删除方法签名相比,这可能是最小的混乱。因此,我想如果您需要添加到界面中,可能会导致一些工作。
钻机

Answers:


9

在API的第2版中,您需要Glarg向该接口添加方法。

为什么?

定义用于API的接口具有两个完全不同的角色:

  1. 依赖倒置-您的API使用了此类接口。它们允许客户端代码创建插件等。
  2. 抽象-这些接口由您的API返回,并隐藏返回对象的实现细节。

现在,对于给定版本的API,相同的接口可以同时充当两者。不过,在将来的版本中,这可以解耦。

  1. 您想从使用的界面中提取更多信息。增强性能,或增加灵活性或其他。定义一个可能从旧接口派生的新接口,并构建一个使用该接口的单独方法。AFAIK大多数.NET语言都允许方法重载,因此可以在不增加太多混乱的情况下进行。
  2. 您想“返回更多”,即从您的API中抽象“更丰富”的对象。在这里,您有两种选择:

    • 您可以合理地假设,客户端代码将没有自己的接口实现器。在这种假设下,将扩展添加到现有接口是安全的。
    • 定义一个新接口,如果可能的话,可以从上一个接口派生。如果无法进行这种派生,请创建单独的方法来查询新接口的实例或使用composition:

      interface MyNewInterface extends MyOldInterface { 
           FancyNewInterface getFancyShit();
      }
      

15

DirectX在其接口中添加了版本号。在您的情况下,解决方案将类似于

public interface IFoo2 : IFoo
{
    void Glarg();
}

该API仍将引用IFoo,并且仅在需要IFoo2功能的方法等中引用IFoo2。

如果IFoo2的方法语义不同,API实现应在现有(=版本1)方法中检查IFoo参数对象是否实际实现IFoo2。


3

向您的API添加一个或多个新方法的方式应该不会对现有API产生任何副作用。最重要的是,继续使用旧API的人(好像新API不存在)应该不受其影响。使用旧的API也不会对新的API 产生意外的副作用。

如果API中的任何现有方法都被新方法取代,请不要立即将其删除。将它们标记为已弃用,并提供有关应使用什么的解释。这向您的代码用户发出警告,即将来的版本可能不再支持它,而不是在没有警告的情况下破坏他们的代码。

如果新旧API不兼容并且无法一起使用而没有不希望的副作用,请将它们分开并说明如果要采用新API,则必须完全淘汰旧API。这是不太理想的,因为总会有人尝试同时使用两者,而在无法使用时会感到沮丧。

由于您是专门询问.NET的,因此您可能需要阅读有关.NET中弃用的文章该文章链接到ObsoleteAttribute(在以下示例中使用):

using System;

public sealed class App {
   static void Main() {      
      // The line below causes the compiler to issue a warning:
      // 'App.SomeDeprecatedMethod()' is obsolete: 'Do not call this method.'
      SomeDeprecatedMethod();
   }

   // The method below is marked with the ObsoleteAttribute. 
   // Any code that attempts to call this method will get a warning.
   [Obsolete("Do not call this method.")]
   private static void SomeDeprecatedMethod() { }
}

2

公共接口更改涉及损坏。常见的策略是仅在主要版本上并且在冻结期之后执行这些操作(因此不会一时兴起)。如果您将添加的内容添加到新的界面中(您的实现可以在同一个类中提供这两者),则可以在不破坏客户的情况下逃脱现实。那不是理想的,如果继续这样做,您将一团糟。

但是,通过其他种类的修改(删除方法,更改签名),您将陷入困境。


2
您可以抢先为将来的方法名称保留一个前缀,并警告所有用户不要使用该命名空间,但即使这样做也会导致API不够美观。通常,parent是绝对正确的:方法的删除(通常是添加)破坏现有用户,除了明智地计划之外,您无能为力。
Kilian Foth'3

1

接口是合同,因此不应具有版本控制。如果足球运动员获得新合同会怎样?旧的仍然有效吗?否。如果更改了接口,则合同也将更改,并且先前的合同(接口)不再有效。

尽管您可以使用IFoo2策略,但是当您具有以下条件时,最终将变得混乱:

  • IFoo2
  • IFoo3
  • IFoo4
  • 等等

uck

API是不同的。我给代码库使用。下个月,我给您一个更新的图书馆。正如另一位发布者所说,不要破坏我已经使用的功能,只需添加新的功能/方法即可。

如果要对某些版本进行版本控制,请使用抽象类而不是接口。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.