Wie entwickelt und versioniert man ein Interface?


22

Angenommen, Sie haben eine Schnittstelle IFoo:

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

In Version 2 Ihrer API müssen Sie Glargdieser Schnittstelle eine Methode hinzufügen . Wie können Sie dies tun, ohne Ihre vorhandenen API-Benutzer zu beschädigen und die Abwärtskompatibilität aufrechtzuerhalten? Dies richtet sich hauptsächlich an .NET, kann aber auch auf andere Frameworks und Sprachen angewendet werden.


Sie können ohne Probleme hinzufügen. Die Probleme treten auf, wenn Sie etwas ändern / entfernen, das bereits vorhanden war.
Rig

1
@Rig: Zumindest in C # wird ein Kompilierungsfehler angezeigt, wenn Sie einer Schnittstelle eine Methode hinzufügen und diese nicht zu Klassen hinzufügen, die diese Schnittstelle implementieren.
Malice

Nun, das stimmt. Ich habe mehr an das Benutzerklassenszenario gedacht, das im Vergleich zum Ändern oder Löschen einer Methodensignatur ein minimales Chaos verursachen kann. Ich nehme an, es könnte zu etwas Arbeit führen, wenn Sie Ihre Benutzeroberfläche erweitern müssen.
Rig

Antworten:


9

In Version 2 Ihrer API müssen Sie Glargdieser Schnittstelle eine Methode hinzufügen .

Warum?

Die für die Verwendung mit einer API definierten Schnittstellen haben zwei völlig unterschiedliche Rollen:

  1. Abhängigkeitsinversion - Diese Schnittstellen werden von Ihrer API verwendet. Sie ermöglichen es dem Client-Code, Plugins usw. zu erstellen.
  2. Abstraktion - Diese Schnittstellen werden von Ihrer API zurückgegeben und verbergen die Implementierungsdetails der zurückgegebenen Objekte.

Nun kann für eine bestimmte Version einer API dieselbe Schnittstelle als beide fungieren. In zukünftigen Versionen kann dies jedoch entkoppelt werden.

  1. Sie möchten weitere Informationen aus der von Ihnen verwendeten Schnittstelle extrahieren. Um die Leistung zu verbessern oder die Flexibilität zu erhöhen. Definieren Sie eine neue Schnittstelle, die möglicherweise von der alten abgeleitet ist, und erstellen Sie eine separate Methode, die diese verwendet. AFAIK Die meisten .NET-Sprachen erlauben eine Überladung der Methoden, sodass dies ohne viel Durcheinander geschehen kann.
  2. Sie möchten "mehr zurückgeben", dh die Abstraktion eines "reichhaltigeren" Objekts von Ihrer API. Hier haben Sie zwei Möglichkeiten:

    • Sie können davon ausgehen, dass der Client-Code keine eigenen Implementierer der Schnittstelle hat. Unter dieser Annahme ist es sicher, Ihre Erweiterungen der vorhandenen Schnittstelle hinzuzufügen.
    • Definieren Sie eine neue Schnittstelle, wenn möglich abgeleitet von der vorherigen. Wenn eine solche Ableitung nicht möglich ist, erstellen Sie separate Methoden, um Instanzen der neuen Schnittstelle abzufragen, oder verwenden Sie die Komposition:

      interface MyNewInterface extends MyOldInterface { 
           FancyNewInterface getFancyShit();
      }
      

15

DirectX fügte seinen Schnittstellen Versionsnummern hinzu. In Ihrem Fall wäre die Lösung so ähnlich

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

Die API verweist weiterhin auf IFoo und auf IFoo2 nur in Methoden usw., in denen IFoo2-Funktionalität erforderlich ist.

Die API-Implementierung sollte in vorhandenen (= Version 1) Methoden prüfen, ob ein IFoo-Parameterobjekt IFoo2 tatsächlich implementiert, wenn die Methodensemantik für IFoo2 unterschiedlich ist.


3

Das Hinzufügen einer neuen Methode (oder neuer Methoden) zu Ihrer API sollte so erfolgen, dass keine Nebenwirkungen auf die vorhandene API auftreten. Vor allem sollte jemand, der die alte API weiterhin verwendet, als ob die neue API nicht existiert, davon unberührt bleiben. Die Verwendung der alten API sollte auch keine unerwarteten Nebenwirkungen auf die neue API haben.

Wenn eine der vorhandenen Methoden in der API durch die neuen ersetzt wird, entfernen Sie sie nicht sofort. Markieren Sie sie als veraltet und erläutern Sie, was stattdessen verwendet werden soll. Das gibt Benutzern Ihres Codes die Warnung, dass zukünftige Versionen ihn möglicherweise nicht mehr unterstützen, anstatt ihren Code ohne Warnung zu brechen.

Wenn die neue und die alte API nicht kompatibel sind und nicht ohne unerwünschte Nebenwirkungen zusammenleben können, trennen Sie sie und dokumentieren Sie, dass die alte API vollständig zurückgezogen werden muss, wenn die neue API übernommen werden soll. Dies ist weniger wünschenswert, da es immer jemanden gibt, der versucht, beides zu verwenden, und der frustriert ist, wenn es nicht funktioniert.

Da Sie speziell nach .NET gefragt haben, möchten Sie möglicherweise diesen Artikel über die Veraltetheit in .NET lesen , der auf die ObsoleteAttribute(im folgenden Beispiel verwendete) verweist:

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

Änderungen an der öffentlichen Schnittstelle sind mit Brüchen verbunden. Die übliche Strategie besteht darin, dies nur in Hauptversionen und nach einer Einfrierperiode zu tun (dies geschieht also nicht aus einer Laune heraus). Sie können davonkommen, ohne Ihre Clients zu beschädigen, wenn Sie Ihre Ergänzungen zu einer neuen Schnittstelle hinzufügen (und Ihre Implementierung kann beide in derselben Klasse bereitstellen). Das ist nicht ideal, und wenn du es weiter machst, wirst du ein Durcheinander haben.

Mit anderen Modifikationen (Entfernen von Methoden, Ändern von Signaturen) stecken Sie jedoch fest.


2
Sie können ein Präfix präventiv für zukünftige Methodennamen reservieren und alle Benutzer warnen, dass sie diesen Namespace nicht verwenden sollten, selbst wenn dies zu einer uneleganten API führt. Im Allgemeinen hat der Elternteil vollkommen recht: Das Entfernen (und oft das Hinzufügen) von Methoden führt zum Absturz bestehender Benutzer, und Sie können nichts dagegen tun, außer dies mit Bedacht zu planen.
Kilian Foth

1

Ein Interface ist ein Vertrag, daher sollte es keine Versionierung haben. Was passiert, wenn ein Fußballspieler einen neuen Vertrag bekommt? Ist der alte noch gültig? Nein. Ändert man die Schnittstelle, ändert sich der Vertrag und der vorherige Vertrag (Schnittstelle) ist nicht mehr gültig.

Obwohl Sie die IFoo2-Strategie verwenden könnten, wird dies mit der Zeit unübersichtlich, wenn Sie:

  • IFoo2
  • IFoo3
  • IFoo4
  • etc.

Yuck.

Eine API ist anders. Ich gebe Bibliothek von Code zu verwenden. Nächsten Monat gebe ich Ihnen eine aktualisierte Bibliothek. Wie in einem anderen Poster bereits gesagt wurde, brechen Sie nicht das, was ich bereits benutze, sondern fügen Sie einfach neue Funktionen / Methoden hinzu.

Wenn Sie etwas versionieren möchten, verwenden Sie eine abtract-Klasse anstelle einer Schnittstelle.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.