Как вы развиваете и версии интерфейса?

22

Скажем, у вас есть интерфейс IFoo:

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

Во второй версии API вы должны добавить метод Glargк этому интерфейсу. Как это сделать, не нарушая существующих пользователей API и не поддерживая обратную совместимость? Это в основном нацелено на .NET, но может применяться и к другим фреймворкам и языкам.

thecoop
источник
Вы можете добавить без проблем. Проблемы возникают, когда вы меняете / удаляете то, что уже было там.
Рог
1
@Rig: По крайней мере, в C # вы получите ошибку компиляции, если добавите метод в интерфейс и не добавите его в классы, которые реализуют этот интерфейс.
Malice
Ну, это правда. Я больше думал о сценарии пользовательского класса, который может иметь минимальный хаос по сравнению с изменением сигнатуры метода или его удалением. Поэтому я полагаю, что это может привести к некоторой работе, если вам нужно будет добавить в свой интерфейс.
Рог

Ответы:

9

Во второй версии API вы должны добавить метод Glargк этому интерфейсу.

Зачем?

Интерфейсы, определенные для использования с API, выполняют две совершенно разные роли:

  1. Инверсия зависимостей - такие интерфейсы используются вашим API. Они позволяют клиентскому коду создавать плагины и т. Д.
  2. Абстракция - такие интерфейсы возвращаются вашим API и скрывают детали реализации возвращаемых объектов.

Теперь для данной версии API один и тот же интерфейс может действовать как оба. Тем не менее, в будущих версиях это можно отделить.

  1. Вы хотите извлечь больше информации из интерфейса, который вы используете. Чтобы повысить производительность или добавить гибкость или что-то еще. Определите новый интерфейс, возможно, производный от старого, и создайте отдельный метод, использующий его. AFAIK большинство языков .NET допускают перегрузку методов, так что это может происходить без особых помех.
  2. Вы хотите «вернуть больше», то есть абстрагировать «более богатый» объект от вашего API. Здесь у вас есть два варианта:

    • Можно разумно предположить, что клиентский код не будет иметь собственных разработчиков интерфейса. При этом предположении безопасно добавлять ваши расширения к существующему интерфейсу.
    • Определите новый интерфейс, если возможно, производный от предыдущего. Если такой вывод невозможен, создайте отдельные методы для запросов на экземпляры нового интерфейса или используйте композицию:

      interface MyNewInterface extends MyOldInterface { 
           FancyNewInterface getFancyShit();
      }
      
back2dos
источник
15

DirectX добавил номера версий к своим интерфейсам. В вашем случае решение будет что-то вроде

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

API все равно будет ссылаться на IFoo и IFoo2 только в методах и т. Д., Где требуется функциональность IFoo2.

Реализация API должна проверять в существующих (= версия 1) методах, действительно ли объект параметра IFoo реализует IFoo2, если семантика метода отличается для IFoo2.

devio
источник
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. В общем, родители абсолютно прав: удаление (и часто прибавление) методы будут нарушать существующие пользователь, и нет ничего , что вы можете делать , что кроме плана его с умом.
Килиан Фот
1

Интерфейс - это контракт, поэтому он не должен иметь версий. Что произойдет, если футболист получит новый контракт? Старый все еще действителен? Нет. Если изменить интерфейс, контракт изменится, и предыдущий контракт (интерфейс) перестанет действовать.

Хотя вы можете использовать стратегию IFoo2, в конечном итоге это станет грязным, если у вас есть:

  • IFoo2
  • IFoo3
  • IFoo4
  • и т.п.

Фу.

API отличается. Я даю библиотеку кода для использования. В следующем месяце я дам вам обновленную библиотеку. Как сказал другой автор, не ломайте то, что я уже использую, просто добавьте новые функции / методы.

Если вы хотите что-то сделать версией, используйте интерфейс abtract вместо интерфейса.

Джон Рейнор
источник