Дизайн API: конкретный или абстрактный подход - лучшие практики?

25

При обсуждении API-интерфейсов между системами (на бизнес-уровне) в нашей команде часто бывают две разные точки зрения: некоторые люди предпочитают более общий, скажем так, общий абстрактный подход, а другие - прямой «конкретный» подход.

Пример: дизайн простого API «поиск человека». конкретная версия будет

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

Люди в пользу конкретной версии говорят:

  • API самодокументируется
  • это легко понять
  • это легко проверить (компилятор или как веб-сервис: проверка схемы)
  • ПОЦЕЛУЙ

Другая группа людей в нашей команде сказала бы: «Это просто список критериев поиска».

searchPerson(List<SearchCriteria> criteria)

с

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

с возможностью создания «параметра» некоторого типа перечисления.

Сторонники говорят:

  • без изменения (декларации) API реализация может измениться, например, добавив больше критериев или больше опций. Даже без синхронизации такого изменения во время развертывания.
  • документация необходима даже при конкретном варианте
  • валидация схемы переоценена, часто вам приходится валидировать дальше, схема не может обработать все случаи
  • у нас уже есть похожий API с другой системой - повторное использование

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

  • много документации о допустимых параметрах и допустимых комбинациях параметров
  • больше коммуникационных усилий, потому что это труднее понять для других команд

Есть ли лучшие практики? Литература?

Erik
источник
3
повторение «String name / name, boolean soundEx» является явным нарушением dry и предполагает, что в этом проекте не учитывается тот факт, что name, как ожидается, согласуется со soundEx. Столкнувшись с такими простыми ошибками в дизайне, трудно перейти к более сложному анализу
комнат
Противоположность «конкретному» - это не «общий», а «абстрактный». Абстракция очень важна для библиотеки или API, и в этом обсуждении не задается действительно фундаментальный вопрос, вместо этого он сосредоточен на том, что, откровенно говоря, довольно тривиальный вопрос стиля. FWIW, контраргументы для опции B звучат как загрузка FUD, вам не нужно никакой дополнительной документации или связи, если дизайн API даже наполовину чист и соответствует принципам SOLID.
Аарона
@Aaronaught, спасибо за указание на это («абстрактный»). Это может быть проблема с переводом, «generisch» на немецком языке все еще хорошо для меня. Что для вас является «действительно фундаментальным вопросом»?
Эрик
4
@Aaronaught: Вопрос не об абстрактном. Правильным исправлением будет то, что противоположность «универсальному» является «конкретным», а не «конкретным».
Ян Худек
Еще один голос за то, что это не общее, а абстрактное, а общее. Приведенный выше «конкретный» пример на самом деле относится к name, firstName и dateOfBirth, другой пример является общим для любых параметров. Ни один из них не является особенно абстрактным. Я бы отредактировал заголовок, но я не хочу начинать редактировать войну :-)
Мэтт Фрейк

Ответы:

18

Это зависит от того, сколько полей вы говорите, и как они используются. Бетон предпочтителен для высокоструктурированных запросов только с несколькими полями, но если запрос имеет тенденцию быть очень свободной формой, то конкретный подход быстро становится громоздким с более чем тремя или четырьмя полями.

С другой стороны, очень сложно поддерживать чистоту общего API. Если вы выполняете простой поиск по именам во многих местах, в конце концов, кто-то устает повторять те же пять строк кода, и они собираются обернуть его в функцию. Такой API-интерфейс неизменно превращается в гибрид общего запроса вместе с конкретными оболочками для наиболее часто используемых запросов. И я не вижу в этом ничего плохого. Это дает вам лучшее из обоих миров.

Карл Билефельдт
источник
7

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

Редко что-то новое происходит под луной. Взгляните на уровень техники, в особенности на установленные стандарты и форматы (например, многие вещи можно смоделировать после каналов, описания событий были разработаны в формате ical / vcal). Сделайте ваш API легко аддитивным, когда частые и вездесущие сущности являются конкретными, а предполагаемые расширения - словарями. Есть также несколько устоявшихся моделей для решения конкретных ситуаций. Например, обработка HTTP-запроса (и аналогичного) может быть смоделирована в API с объектами Request и 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 моим бизнес-требованиям? У меня есть четко определенные критерии успеха? Может ли он масштабироваться?». Похоже, что это действительно простые лучшие практики, которым нужно следовать, но, честно говоря, они гораздо важнее конкретных, чем общих.

gws2
источник
1

Я бы не сказал, что абстрактный API обязательно сложнее проверить. Если параметры критерия достаточно просты и имеют небольшие зависимости друг от друга, не имеет значения, передаете ли вы параметры отдельно или в массиве. Вам все еще нужно проверить их все. Но это зависит от дизайна параметров критериев и самих объектов.

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

Pavels
источник
1

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

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

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

Ян Худек
источник
1

Я бы предпочел подход абстрактного интерфейса. Задание запроса к этим видам (поисковой) службы является распространенной проблемой и, скорее всего, произойдет снова. Кроме того, вы, вероятно, найдете больше кандидатов на обслуживание, подходящих для повторного использования более общего интерфейса. Чтобы обеспечить согласованный общий интерфейс для этих служб, я бы не перечислял идентифицированные в настоящее время параметры запроса в определении интерфейса.

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

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

0x0me
источник
1

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

Хорошие API

  • Невозможно ошибиться.
  • Компилятор / компоновщик не позволит вам ошибиться.
  • Компилятор предупредит вас, если вы ошиблись.
  • Очевидное использование (вероятно) правильное.
  • Название говорит вам, как его использовать.
  • Сделайте это правильно, иначе он всегда сломается во время выполнения.
  • Следуйте общему соглашению, и вы поймете это правильно.
  • Прочитайте документацию, и вы все поймете правильно.
  • Прочитайте реализацию, и вы все поймете правильно.
  • Прочитайте правильную ветку списка рассылки, и вы все поймете правильно.

Плохие API

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

Обе подробные страницы здесь и здесь содержат подробное обсуждение каждого пункта. Это действительно необходимо прочитать для дизайнеров API. Спасибо Расти, если ты когда-нибудь прочитал это.

JensG
источник
0

По словам непрофессионала:

  • Абстрактный подход имеет то преимущество, что позволяет строить конкретные методы вокруг него.
  • Обратное не верно
Тулаинс Кордова
источник
UDP имеет то преимущество, что позволяет создавать собственные надежные потоки. Так почему же почти все используют TCP?
svick
Также рассматривается большинство вариантов использования. Некоторые случаи могут понадобиться так часто, что возможно сделать эти случаи особенными.
Роман Суси
0

Если вы SearchCriteriaнемного расширите идею, это может дать вам гибкость, такую ​​как критерии создания ANDи ORт. Д. Если вам нужна такая функциональность, это был бы лучший подход.

В противном случае разработайте его для удобства использования. Сделайте API простым для людей, которые его используют. Если у вас есть некоторые основные функции, которые часто нужны (например, поиск человека по имени), предоставьте их напрямую. Если опытным пользователям нужны расширенные поиски, они все равно могут использовать SearchCriteria.

UOOO
источник
0

Что делает код API? Если это что-то гибкое, то гибкий API - это хорошо. Если код, стоящий за API, очень специфичен, то наложение на него гибкой грани означает, что пользователи API будут разочарованы и раздражены всем, что притворяется API, возможно, но на самом деле это невозможно.

Для вашего примера поиска человека все три поля обязательны для заполнения? Если так, то список критериев плох, потому что он допускает множество вариантов использования, которые просто не работают. Если нет, то требование пользователя указывать необязательные входные данные - это плохо. Насколько вероятно, что поиск по адресу будет добавлен в V2? Гибкий интерфейс облегчает добавление, чем негибкий.

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

stonemetal
источник