Як ви розвиваєте та використовуєте версію інтерфейсу?


22

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

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

У версії 2 свого API вам потрібно додати метод Glargдо цього інтерфейсу. Як це зробити, не порушуючи існуючих користувачів API і не підтримуючи сумісність назад? Це в основному спрямовано на .NET, але може застосовуватися і до інших фреймворків та мов.


Ви можете додати без проблем. Проблеми виникають, коли ви змінюєте / видаляєте щось, що вже було.
Ріг

1
@Rig: У C # принаймні ви отримаєте помилку компіляції, якщо додати метод до інтерфейсу і не додати його до класів, які реалізують цей інтерфейс.
Маліса

Ну, це правда. Я думав більше про сценарій класу користувача, який може бути меншим хаосом у порівнянні зі зміною підпису методу або видаленням. Тому я припускаю, що це може призвести до певної роботи, якщо вам потрібно буде додати свій інтерфейс.
Риг

Відповіді:


9

У версії 2 свого API вам потрібно додати метод Glargдо цього інтерфейсу.

Чому?

Інтерфейси, визначені для використання з API, мають дві абсолютно різні ролі:

  1. Інверсія залежності - такі інтерфейси споживаються вашим API. Вони дозволяють клієнтському коду створювати додатки тощо.
  2. Абстракція - такі інтерфейси повертаються вашим API і приховують деталі реалізації повернених об'єктів.

Тепер для даної версії API той самий інтерфейс може діяти як обидва. Але все ж у майбутніх версіях це можна відключити.

  1. Ви хочете отримати більше інформації з інтерфейсу, який ви споживаєте. Для підвищення продуктивності або додавання гнучкості чи будь-чого іншого. Визначте новий інтерфейс, можливо, похідний від старого та побудуйте окремий метод, що його споживає. Більшість мов AFAIK .NET дозволяють перевантажувати методи, тому це може статися без додавання суворості.
  2. Ви хочете "повернути більше", тобто абстрагування "багатшого" об'єкта від вашого API. Тут у вас є два варіанти:

    • Можна обгрунтовано припустити, що клієнтський код не матиме власних реалізаторів інтерфейсу. За цим припущенням можна безпечно додати розширення до наявного інтерфейсу.
    • Визначте новий інтерфейс, якщо це можливо, похідний від попереднього. Якщо таке виведення неможливе, створіть окремі методи для запиту про екземпляри нового інтерфейсу або використовуйте композицію:

      interface MyNewInterface extends MyOldInterface { 
           FancyNewInterface getFancyShit();
      }
      

15

DirectX додав номери версій до своїх інтерфейсів. У вашому випадку рішення було б щось подібне

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

API все ще посилається на IFoo, а на IFoo2 лише у методах тощо, де необхідна функціональність IFoo2.

Реалізація API повинна перевірити існуючі (= версія 1) методи, чи об'єкт параметра IFoo реалізує IFoo2, чи семантика методу відрізняється від 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. Загалом, батьки абсолютно прав: видалення (і часто додаток) методи будуть порушувати існуючі користувач, і немає нічого , що ви можете робити , що крім плану його з розумом.
Кіліан Фот

1

Інтерфейс - це контракт, тому він не повинен мати версії. Що станеться, якщо футболіст отримає новий контракт? Чи справді стара? Ні. Якщо ви змінюєте інтерфейс, контракт змінюється і попередній договір (інтерфейс) більше не діє.

Хоча ви можете використовувати стратегію IFoo2, врешті-решт це стане безладним, коли у вас є:

  • IFoo2
  • IFoo3
  • IFoo4
  • тощо.

Гидота.

API відрізняється. Я даю бібліотеці коду користуватися. Наступного місяця я передаю вам оновлену бібліотеку. Як сказав інший плакат, не порушуйте те, що я вже використовую, просто додайте нові функціональні можливості / методи.

Якщо ви хочете щось версії, замість інтерфейсу використовуйте клас абстракції.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.