Как создать хороший API

api

Хороший API — это немаловажная часть успеха. Бывает, что это единственная причина, почему выбрана именно эта библиотека или этот продукт. Создать хороший API не так-то просто, и не существует однозначного пути для его создания, потому что для каждого отдельного случая нужен свой подход. Те, кому нередко приходилось использовать различные библиотеки, наверняка уже неплохо представляют, что именно представляет собой хороший API. Постараемся собрать все точки зрения воедино.

1. Универсальный, но удобный

Универсальность – это понятно каждому, но вот, что значит удобно, понятно не всем. К некоторым элементам приходится обращаться чаще, чем к другим. Поэтому ключом к разработке грамотного API может стать создание выдержки из полной документации для справки по часто используемых методам.
Например, давайте рассмотрим простой класс Person. У него есть:

  • Вам нужен пустой конструктор, который создает пустой объект, поля которого будут заполнены позже;
  • Необходимы set/get методы для работы с именем и фамилией;
  • И методы для обработки дополнительных полей имени/фамилии (у некоторых людей бывает больше двух слов в имени).

Из описания всех этих методов можно составить полный API. А для удобства давайте отдельно выделим документацию по таким элементам:

  • Конструктор с двумя параметрами: именем и фамилией, т.к. такая комбинация наиболее часто встречающаяся в мире;
  • Set/get методы для дополнительных имен, т.к. в некоторых культурах мира большая часть людей имеет более одного имени. Если же приложение создается специально для таких стран, то в конструктор из первого пункта имеет смысл добавить еще параметры.

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

2. Применимый в любом случае, но не слишком большой

Этот пункт, пожалуй, можно было бы объединить с первым, но хотелось бы все же уделить ему больше внимания.
Большая часть документации строится по принципу «А что, если…» Хотя здесь, пожалуй, необходимо себя ограничивать, потому что нет определенных границ для рассмотрения всех возможных случаев. Точно не скажешь, от чего именно стоит избавиться, ибо все зависит от конкретной ситуации. Между тем, вот несколько советов:

  • То, на чем многое завязано, что не менялось в течение долгого времени, следует обновлять постепенно, желательно, предупреждая об этом заранее.

Например, IPv6. Мы так долго пересаживаемся на новый протокол, т.к. большое количество программного обеспечения строго привязано к IPv4. Правильно ли это? Пожалуй, да. Сейчас у программиста имеется выбор, использовать ли современную технологию или же даже реализовать одновременную поддержку различных протоколов. Стоит ли это потраченного времени? Решать вам.

  • Удобство классов и методов сохраняется до тех пор, пока понятно, что эти классы или методы из себя представляют и за что отвечают.

3. Названия методов должны быть осмысленными, доступными для понимания, структурированные и по возможности короткие

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

Структурирование API также имеет большое значение, если документация достаточно обширна. Говоря короче, должны существовать какие-то правила именования с использованием суффиксов/префиксов, которые отделят ваш API от остальной документации.

Кроме того, логичным название метода можно сделать за счет целостности. Правила именования должны быть общими для всего API, и желательно сходными с правилами именования в документации аналогичных областей. Лучше подстраиваться сразу под стиль именования языка, для которого вы создаете свой модуль и пишете API.

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

4. Ориентирован на повышение производительности, но без потери удобства

К некоторым API не приходится обращаться часто, другие же должны всегда быть под рукой, а потому не пользование ими не должно снижать производительности программиста. И при этом никогда нельзя забывать об удобстве. Например, API Windows – пример того, как удобством пожертвовали в пользу производительности. Ему не хватает функций, заполняющих структуры данных значениями по умолчанию. Раздражает обнулять каждый элемент отдельно. С другой стороны, некоторые функции можно было бы назвать лишними: иногда несколько функций очевидно стоило бы заменить на одну. К примеру, FindFirstFile() и FindNextFile() можно было бы объединить, а вот ZeroMemory() стоило бы заменить на более эффективную memset().

5. Удобные константы

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

6. Правильно выбирайте формат файла для представления вашего API

Коротко говоря – не останавливайтесь слепо на XML.
Если вы работаете над веб-сервисом, то задумайтесь, в каком виде представить документацию: это может быть XML, JSON или что-то еще. Подумайте о тех, кто будет пользоваться вашим API. Заставлять людей использовать XML это… ну, ладно, используйте XML, если ничто другое вас не устраивает.

7. Настраиваемый, но в меру

Здесь речь идет о фреймворках. Многие из них позволяют пользователю изменять поведение некоторых элементов за счет изменения параметров конфигурации. Это хорошо, но надо знать меру. Во-первых, необходимо понимать, что какие-то конфигурации совершенно бестолковы. Чем больше параметров поддается изменению, тем сложнее учесть все возможные конфигурации, не говоря уже о том, что какие-то значения параметров могут оказаться несовместимы и неблаготворно скажутся на работоспособности.
«Абсолютно все можно подключить, расширить и настроить» – не самый верный подход. Лучше не иметь возможности настроить, чем настраивать так, чтобы ничего не работало.
В особенных случаях можно ограничить возможности конфигурирования «белым ящиком»: вместо увеличения количества комбинаций параметров по принципу «черного ящика», можно разбить все настройки на группы заранее выставленных значений, и пользователь будет подстраивать фреймворк под себя, комбинируя эти наборы настроек.

8. Одновременное выполнение vs. Однопоточность

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

9. Не все знают английский язык

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

10. Совместимость со старыми и новыми версиями

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

  • Изначально разрабатывайте API так, чтобы его можно было расширить в будущем. Особенно будьте осторожны с аргументами логического типа (их можно заменить нумерованным типом);
  • Если о каких-то фичах, которые будут введены в будущем, вам известно заранее, то вы можете сразу же включить их в API для обеспечения совместимости с будущими версиями;
  • Внимательно следите за сохранением совместимости с предыдущими версиями, но не реализовывайте рефлексию своего API, потому что в будущем вам придется учитывать и совместимость с механизмом самообработки API;
  • Лучше делайте большие перерывы между выпусками новых версий API, чем выпускайте частые обновления. Конечно, никто не любит подобных перерывов, но вот необходимость часто обновляться, кажется, особенно раздражает разработчиков;
  • Не пренебрегайте альфа/бета-тестированиями, потому что лучший путь развития – это развитие в соответствии с откликами пользователей.

11. Излишнее удобство не оправдано

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

Заключение

Хорошие API не могут быть созданы за закрытой дверью несколькими, пусть даже и очень умными и опытными людьми. Общение с потребителями, особенно, если такой контакт налажен с самого начала работы — это ключ к грамотной реализации.

Антон Машков, главный редактор