Дизайн API: конкретний та абстрактний підхід - найкращі практики?


25

Обговорюючи API між системами (на рівні бізнесу), в нашій команді часто існують дві різні точки зору: деякі люди віддають перевагу більш - скажімо, - загальний абстрактний підхід, інші - прямо "конкретний" підхід.

Приклад: розробка простого API "пошуку людей". конкретна версія була б

 searchPerson(String name, boolean soundEx,
              String firstName, boolean soundEx,
              String dateOfBirth)

Люди, які виступають за конкретну версію, кажуть:

  • API самодокументує
  • це легко зрозуміти
  • його легко перевірити (компілятор або як веб-сервіс: перевірка схеми)
  • KISS

Інша група людей в нашій команді сказала б "Це лише список критеріїв пошуку"

searchPerson(List<SearchCriteria> criteria)

з

SearchCritera {
  String parameter,
  String value,
  Map<String, String> options
}

з можливим створенням "параметра" деякого типу перерахування.

Прихильники кажуть:

  • без зміни (декларації) API реалізація може змінюватися, наприклад, додаючи більше критеріїв або більше варіантів. Навіть без синхронізації такої зміни під час розгортання.
  • документація необхідна навіть при конкретному варіанті
  • Перевірка схеми завищена, часто доводиться додатково перевіряти, схема не може обробляти всі випадки
  • у нас вже є подібний API з іншою системою - повторне використання

Контраргумент є

  • багато документації про дійсні параметри та допустимі комбінації параметрів
  • більше зусиль для спілкування, оскільки це складніше зрозуміти іншим командам

Чи є найкращі практики? Література?


3
повторне "Рядок ім'я / ім'я, булевий звукEx" є явним порушенням сухого і наводить на думку, що ця конструкція не змогла вирішити той факт, що ім'я, як очікується, піде разом із soundEx. Зіткнувшись з такими простими помилками в дизайні, важко продовжувати складніший аналіз
гнат

Протилежність "конкретному" не є "загальним", це "абстрактним". Абстракція дуже важлива для бібліотеки чи API, і в цій дискусії не вдається задати по-справжньому фундаментальне питання, натомість зупинившись на тому, що, відверто кажучи, досить тривіальне питання про стиль. FWIW, контр-аргументи для варіанту B звучать як навантаження FUD, вам не потрібно мати додаткової документації або повідомлення, якщо дизайн API навіть напівчистий і відповідає принципам SOLID.
Aaronaught

@Aaronaught дякую, що вказав на це ("абстрактно"). Це може бути проблема перекладу, "generisch" німецькою мовою все ще звучить нормально. Що для вас "справді фундаментальне питання"?
erik

4
@Aaronaught: Питання не в абстрактному. Правильною корекцією було б те, що протилежне "родовому" є "конкретним", а не "конкретним".
Ян Худек

Ще один голос за це стосується не загального проти абстрактного, а загального проти конкретного. Наведений вище "конкретний" приклад фактично специфічний для імені, firstName та dateOfBirth, інший приклад - загальний для будь-яких параметрів. Жоден з них не є особливо абстрактним. Я б редагував заголовок, але я не хочу починати війну з редагуванням :-)
matt freake

Відповіді:


18

Це залежить від того, скільки полів ви говорите і як вони використовуються. Бетон є кращим для високоструктурованих запитів із лише кількома полями, але якщо запит має дуже вільну форму, то конкретний підхід швидко стає непростим із більш ніж трьома-чотирма полями.

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


7

Розробка хорошого API - це мистецтво. Хороший API цінується навіть через час. На мою думку, на абстрактно-конкретній лінії не повинно бути загальних упереджень. Деякі параметри можуть бути такими ж конкретними, як дні тижня, деякі потребують розробки для розширюваності (і досить нерозумно робити їх конкретними, наприклад, частинами назв функцій), а інші можуть піти ще далі і для того, щоб мати елегантний API потрібно забезпечити зворотні дзвінки чи навіть конкретну доменну мову допоможе боротися зі складністю.

Під Місяцем рідко трапляються нові речі. Погляньте на рівень техніки, особливо на встановлені стандарти та формати (наприклад, багато речей можна змоделювати після каналів, описи подій були розроблені в ical / vcal). Зробіть свій API легко добавним, коли часті та всюдисущі сутності є конкретними, а передбачені розширення - словниками. Існує також кілька усталених моделей поводження з конкретними ситуаціями. Наприклад, обробка HTTP-запиту (і подібного) може бути змодельована в API з об'єктами Request and Response.

Перш ніж розробляти API, мозковий штурм стосується аспектів, включаючи ті, які не будуть включені, але ви повинні знати. Прикладами таких є мова, напрям написання, кодування, локаль, інформація про часовий пояс тощо. Зверніть увагу на місця, де можуть з’являтися кратні: використовувати список, а не єдине значення для них. Наприклад, якщо ви шукаєте API для системи відеочатів, ваш API буде набагато кориснішим, якщо ви вважаєте, що N учасників, а не лише двоє (навіть якщо ваші характеристики на даний момент такі).

Іноді бути абстрактним допомагає різко зменшити складність: навіть якщо ви спроектуєте калькулятор для додавання лише 3 + 4, 2 + 2 та 7 + 6, реалізувати X + Y може бути набагато простіше (з технічно можливими межами на X та Y, і додайте ADD (X, Y) до вашого API замість ADD_3_4 (), ADD_2_2 (), ...

Загалом, вибір того чи іншого способу - це лише технічна деталь. Ваша документація повинна конкретно описувати випадки частого використання.

Що б ви не робили з боку структури даних, введіть поле для версії API.

Підводячи підсумок, API повинен мінімізувати складність при роботі з вашим програмним забезпеченням. Щоб оцінити API, рівень викритої складності повинен бути адекватним. Вирішення форми API багато в чому залежать від стабільності проблемної області. Таким чином, слід зробити деяку оцінку, в якому напрямку буде рости програмне забезпечення та його API, оскільки ця інформація може впливати на рівняння складності. Також розробка API є для того, щоб люди зрозуміли. Якщо є якісь хороші традиції в галузі програмних технологій, в якій ви перебуваєте, намагайтеся не сильно відступати від них, оскільки це допоможе зрозуміти. Враховуйте, для кого ви пишете. Більш просунуті користувачі оцінять загальність та гнучкість, тоді як тим, хто має менше досвіду, може бути зручніше з конкретиками. Однак піклуйтеся про більшість користувачів API,

Щодо літератури, я можу порекомендувати провідних програмістів "Красивого коду" пояснити, як вони думають Енді Орам, Грег Вілсон, оскільки я думаю, що краса полягає у сприйнятті прихованої оптимальності (і придатності до певної мети).


1

Мої особисті переваги полягають у тому, щоб бути абстрактним, але політика моєї компанії зациклювалася на тому, щоб бути конкретним. Ось і закінчився дебат для мене :)

Ви зробили хорошу роботу з переліком плюсів і мінусів для обох підходів, і якщо ви продовжуєте копати, ви знайдете безліч аргументів на користь обох сторін. Поки архітектура вашого API розроблена належним чином - це означає, що ви замислювалися над тим, як він буде використовуватися сьогодні та як він може розвиватися та зростати в майбутньому - тоді вам слід добре.

Ось дві закладки у мене з протилежними точками зору:

Улюблені абстрактні заняття

Улюблені інтерфейси

Запитайте себе: "Чи відповідає API моїм бізнес-вимогам? Чи я чітко визначені критерії успіху? Чи може він масштабуватися?". Вони здаються простими найкращими практичними методами, але, якщо чесно, вони набагато важливіші, ніж конкретні та загальні.


1

Я б не сказав, що абстрактний API обов'язково складніше перевірити. Якщо параметри критерії досить прості і мають невеликі залежності між собою, це не має великої різниці, передаючи параметри окремо або в масив. Потрібно все-таки підтвердити їх. Але це залежить від проектування параметрів критеріїв та самих об’єктів.

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


1

Аргумент зміни слід відхилити з YAGNI. В основному, якщо ви фактично не маєте принаймні 3 різних випадків використання, які по-різному використовують загальний API, шанси досить низькі, ви спроектуєте його так, що він не повинен буде змінюватися при появі наступного випадку використання (і коли у вас є випадків, очевидно, вам потрібен загальний інтерфейс, період). Тому не намагайтеся бути готовими до змін.

Зміни не потрібно синхронізувати для розгортання в будь-якому випадку. Коли ви згодом інтерфейсуєте, ви завжди можете надати більш специфічний інтерфейс для зворотної сумісності. Але на практиці будь-яке розгортання буде мати стільки змін, що ви все одно синхронізуєте його, тому вам не доведеться тестувати проміжні стани. Я також не вважав би це аргументом.

Щодо документації, будь-яке рішення може бути простим у використанні та очевидним. Але це є важливим аргументом. Реалізуйте інтерфейс, щоб його було легко використовувати у фактичних випадках. Іноді конкретні можуть бути кращими, а інколи загальними.


1

Я б віддав перевагу абстрактному підходу інтерфейсу. Поставити запит до таких видів (пошукової) служби - це поширена проблема і, швидше за все, повториться. Крім того, ви, можливо, знайдете більше кандидатів на обслуговування, які підходять для повторного використання більш загального інтерфейсу. Щоб мати можливість забезпечити узгоджений загальний інтерфейс для цих служб, я б не перераховував поточно визначені параметри запиту у визначенні інтерфейсу.

Як було зазначено раніше - мені подобається можливість змінити або розширити реалізацію без зміни інтерфейсу. Додавання інших критеріїв пошуку не повинно відображатися у визначенні служби.

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


1

Найкращий підсумок, який я коли-небудь бачив, - це масштабність Расті, яка тепер називається маніфестом API дизайну Расті . Я можу лише настійно рекомендувати це. Для повноти я наводжу підсумок шкали з першого посилання (чим краще зверху, тим гірше внизу):

Хороші API

  • Помилитися неможливо.
  • Компілятор / лінкер не дасть вам помилитися.
  • Компілятор попередить, якщо ви помилилися.
  • Очевидне використання - це, мабуть, правильне.
  • Назва говорить про те, як ним користуватися.
  • Зробіть це правильно, інакше це завжди зламається під час виконання.
  • Дотримуйтесь загальної конвенції, і ви все отримаєте правильно.
  • Прочитайте документацію, і ви все отримаєте правильно.
  • Прочитайте реалізацію, і ви все зрозумієте.
  • Прочитайте правильну тему списку розсилки, і ви все отримаєте правильно.

Неправильні API

  • Прочитайте нитку списку розсилки, і ви помилитесь.
  • Прочитайте реалізацію, і ви помилитесь.
  • Прочитайте документацію, і ви помилитесь.
  • Дотримуйтесь загальної конвенції, і ви помилитесь.
  • Зробіть це правильно, а іноді воно порушиться під час виконання.
  • Назва говорить вам, як не користуватися ним.
  • Очевидне використання неправильне.
  • Компілятор попередить, якщо ви правильно це зробите.
  • Компілятор / лінкер не дасть вам правильно це зробити.
  • Поправитись неможливо.

Обидві сторінки деталей тут і тут містять поглиблене обговорення кожного пункту. Це дійсно обов’язково читати для дизайнерів API. Дякую Расті, якщо ти коли-небудь це читав.


0

Словами мирянина:

  • Абстрактний підхід має перевагу в тому, що дозволяє будувати навколо нього конкретні методи.
  • Навпаки - це неправда

Перевага UDP має можливість створювати власні надійні потоки. То чому майже всі користуються TCP?
svick

Існує також розгляд більшості випадків використання. Деякі випадки можуть знадобитися настільки часто, що ці справи можна зробити особливими.
Роман Сусі

0

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

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


0

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

Для прикладу пошуку для вашої людини потрібні всі три поля? Якщо так, то список критеріїв поганий, оскільки він дозволяє використовувати безліч застосувань, які просто не працюють. Якщо ні, то вимагати від користувача вказувати необов’язкові входи - це погано. Наскільки ймовірно, що пошук за адресою буде доданий у V2? Гнучкий інтерфейс дозволяє легше додати, ніж негнучкий.

Не кожна система повинна бути надзвичайно гнучкою, намагаючись зробити все так, як архітектура космонавтів. Гнучкий лук стріляє стрілами. Гнучка шабля така ж корисна, як і гумова курка.

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