mirror of
https://github.com/twirl/The-API-Book.git
synced 2025-01-05 10:20:22 +02:00
style fix
This commit is contained in:
parent
4070313067
commit
65153b205b
40
API.ru.md
40
API.ru.md
@ -30,17 +30,21 @@
|
||||
|
||||
Иными словами, десятки, если не сотни, различных API должны отработать корректно для выполнения базовых действий типа просмотра web-страницы; без надёжной работы каждого из них современные информационные технологии попросту не могли бы существовать.
|
||||
|
||||
API — _это обязательство_. Формальное обязательство связывать между собой различные программируемые контексты.
|
||||
**API — это обязательство**. Формальное обязательство связывать между собой различные программируемые контексты.
|
||||
|
||||
Когда меня просят привести пример хорошего API, я обычно показываю фотографию римского виадука:
|
||||
* он связывает между собой две области
|
||||
* обратная совместимость нарушена ноль раз за последние две тысячи лет.
|
||||
|
||||
Отличие римского виадука от хорошего API состоит лишь в том, что API предлагает _программный_ контракт. Для связывания двух областей необходимо написать некоторый _код_. Цель этой книги — помочь вам написать код, так же хорошо выполняющий свою задачу, как и римский виадук.
|
||||
Отличие римского виадука от хорошего API состоит лишь в том, что API предлагает _программный_ контракт. Для связывания двух областей необходимо написать некоторый _код_. Цель этой книги — помочь вам разработать API, так же хорошо выполняющий свою задачу, как и римский виадук.
|
||||
|
||||
Виадук также хорошо иллюстрирует другую проблему разработки API: вашими пользователями являются инженеры. Вы не поставляете воду напрямую потребителю: к вашей инженерной мысли подключаются заказчики путём пристройки к ней каких-то своих инженерных конструкций. С одной стороны, вы можете обеспечить водой гораздо больше людей, нежели если бы вы сами подводили трубы к каждому крану. С другой — качество инженерных решений заказчика вы не может контролировать, и проблемы с водой, вызванные некомпетентностью подрядчика, неизбежно будут валить на вас.
|
||||
|
||||
Поэтому проектирование API налагает на вас несколько большую ответственность; API выступает как мультипликатором ваших возможностей, так и мультипликатором ваших ошибок.
|
||||
Поэтому проектирование API налагает на вас несколько большую ответственность. **API является как мультипликатором ваших возможностей, так и мультипликатором ваших ошибок**.
|
||||
|
||||
### Замечание о терминологии
|
||||
|
||||
Разработка программного обеспечения характеризуется, помимо прочего, наличием множества различных парадигм разработки, адепты которых зачастую настроены весьма воинственно. Поэтому при написании этой книги мы намеренно избегаем слов «метод», «объект», «функция» и так далее, используя нейтральный термин «сущность». Под сущностью понимается некоторая атомарная единица функциональности — класс, метод, объект, нужное подчеркнуть.
|
||||
|
||||
## I. Проектирование API
|
||||
|
||||
@ -54,13 +58,13 @@ API — _это обязательство_. Формальное обязате
|
||||
* API должно быть читаемым: в идеале разработчик, просто глядя в номенклатуру методов, сразу пишет правильный код, не углубляясь в детали (особенно — детали реализации!); немаловажно уточнить, что из интерфейсов объектов должно быть понятно не только решение задачи, но и возможные ошибки и исключения;
|
||||
* API должно быть консистентно: при разработке новой функциональности, т.е. при обращении к каким-то незнакомым сущностям в API, разработчик может действовать по аналогии с уже известными ему концепциями API, и его код будет работать.
|
||||
|
||||
Однако статическое удобство и понятность API — это простая часть. В конце концов, никто не стремится специально сделать API нелогичным и нечитаемым — всегда при разработке мы начинаем с каких-то понятных концепций и пытаемся их развивать. При минимальном опыте проектирования сложно сделать ядро API, не удовлетворяющее критериям очевидности, читаемости и консистентности.
|
||||
Однако статическое удобство и понятность API — это простая часть. В конце концов, никто не стремится специально сделать API нелогичным и нечитаемым — всегда при разработке мы начинаем с каких-то понятных базовых концепций. При минимальном опыте проектирования сложно сделать ядро API, не удовлетворяющее критериям очевидности, читаемости и консистентности.
|
||||
|
||||
Проблемы начинаются, когда мы начинаем API развивать. Добавление новой фунциональности рано или поздно приводит к тому, что некогда простое и понятное API становится наслоением разных концепций, а попытки сохранить обратную совместимость приводят к нелогичным, неочевидным и попросту плохим решениям. Отчасти это связано так же и с тем, что невозможно обладать полным знанием о будущем: ваше понимание о «правильном» API тоже будет меняться со временем, как в объективной части (какие задачи решает API и как лучше это сделать), так и в субъективной — что такое очевидность, читабельность и консистентность для вашего API.
|
||||
|
||||
Принципы, которые я буду излагать ниже, во многом ориентированы именно на то, чтобы API правильно развивалось во времени и не превращалось в нагромождение разнородных неконсистентных интерфейсов. Важно понимать, что такой подход тоже небесплатен: необходимость держать в голове варианты развития событий и закладывать возможность изменений в API означает избыточность интерфейсов и возможно излишнее абстрагирование. И то, и другое, помимо прочего, усложняет и работу программиста, пользующегося вашим API; усложнение API, закладывание перспектив «на будущее» имеет смысл, только если это будущее у API есть, иначе это просто оверинжиниринг.
|
||||
Принципы, которые я буду излагать ниже, во многом ориентированы именно на то, чтобы API правильно развивалось во времени и не превращалось в нагромождение разнородных неконсистентных интерфейсов. Важно понимать, что такой подход тоже небесплатен: необходимость держать в голове варианты развития событий и закладывать возможность изменений в API означает избыточность интерфейсов и возможно излишнее абстрагирование. И то, и другое, помимо прочего, усложняет и работу программиста, пользующегося вашим API. **Закладывание перспектив «на будущее» имеет смысл, только если это будущее у API есть, иначе это попросту оверинжиниринг**.
|
||||
|
||||
#### Обратная совсместимость
|
||||
#### Обратная совместимость
|
||||
|
||||
### 1. Пирамида контекстов API
|
||||
|
||||
@ -74,28 +78,28 @@ API — _это обязательство_. Формальное обязате
|
||||
|
||||
Может показаться, что наиболее полезные советы и best practice приведены в последнем разделе, однако это не так; цена ошибки, допущенной на разных уровнях весьма различна. Если исправить плохое именование довольно просто, то исправить неверное понимание того, зачем вообще нужно API, практически невозможно.
|
||||
|
||||
**NB**. Здесь и далее мы будем рассматривать концепции разработки API на примере некоторого гипотетического API заказа кофе в городских кофейнях. На всякий случай сразу уточним, что пример является синтетическим; в реальной ситуации, если бы такое API пришлось проектировать, оно вероятно было бы совсем не похоже на наш выдуманный пример.
|
||||
_NB_. Здесь и далее мы будем рассматривать концепции разработки API на примере некоторого гипотетического API заказа кофе в городских кофейнях. На всякий случай сразу уточним, что пример является синтетическим; в реальной ситуации, если бы такое API пришлось проектировать, оно вероятно было бы совсем не похоже на наш выдуманный пример.
|
||||
|
||||
### Определение области применения
|
||||
|
||||
Ключевой вопрос, который вы должны задать себе четыре раза, выглядит так: какую проблему мы решаем? Задать его следует четыре раза с ударением на каждом из четырёх слов.
|
||||
|
||||
1. _Какую_ проблему мы решаем? Можем ли мы чётко описать, в какой ситуации гипотетическим потребителям-разработчикам нужно наше API?
|
||||
1. _Какую_ проблему мы решаем? Можем ли мы чётко описать, в какой ситуации гипотетическим потребителям-разработчикам нужно наше API?
|
||||
|
||||
2. Какую _проблему_ мы решаем? А мы правда уверены, что описанная выше ситуация — проблема? Действительно ли кто-то готов платить (в прямом и переносном смысле) за то, что ситуация будет как-то автоматизирована?
|
||||
2. Какую _проблему_ мы решаем? А мы правда уверены, что описанная выше ситуация — проблема? Действительно ли кто-то готов платить (в прямом и переносном смысле) за то, что ситуация будет как-то автоматизирована?
|
||||
|
||||
3. Какую проблему _мы_ решаем? Действительно ли решение этой проблемы находится в нашей компетенции? Действительно ли мы находимся в той позиции, чтобы решить эту проблему?
|
||||
3. Какую проблему _мы_ решаем? Действительно ли решение этой проблемы находится в нашей компетенции? Действительно ли мы находимся в той позиции, чтобы решить эту проблему?
|
||||
|
||||
4. Какую проблему мы _решаем_? Правда ли, что решение, которое мы предлагаем, действильно решает проблему? Не создаём ли мы на её месте другую проблему, более сложную?
|
||||
4. Какую проблему мы _решаем_? Правда ли, что решение, которое мы предлагаем, действильно решает проблему? Не создаём ли мы на её месте другую проблему, более сложную?
|
||||
|
||||
Итак, предположим, что мы хотим предоставить API автоматического заказа кофе в городских кофейнях. Попробуем применить к ней этот принцип.
|
||||
|
||||
1. Зачем кому-то может потребоваться API для приготовления кофе? В чем неудобство заказа кофе через интерфейс, человек-человек или человек-машина? Зачем нужна возможность заказа машина-машина?
|
||||
* Возможно, мы хотим решить проблему выбора и знания? Чтобы человек наиболее полно знал о доступных ему здесь и сейчас опциях.
|
||||
* Возможно, мы оптимизируем время ожидания? Чтобы человеку не пришлось ждать, пока его заказ готовится.
|
||||
* Возможно, мы хотим минимизировать ошибки? Чтобы человек получил именно то, что хотел заказть, не потеряв информацию при разговорном общении либо при настройке незнакомого интерфейса кофе-машины.
|
||||
1. Зачем кому-то может потребоваться API для приготовления кофе? В чем неудобство заказа кофе через интерфейс, человек-человек или человек-машина? Зачем нужна возможность заказа машина-машина?
|
||||
* Возможно, мы хотим решить проблему выбора и знания? Чтобы человек наиболее полно знал о доступных ему здесь и сейчас опциях.
|
||||
* Возможно, мы оптимизируем время ожидания? Чтобы человеку не пришлось ждать, пока его заказ готовится.
|
||||
* Возможно, мы хотим минимизировать ошибки? Чтобы человек получил именно то, что хотел заказть, не потеряв информацию при разговорном общении либо при настройке незнакомого интерфейса кофе-машины.
|
||||
|
||||
Вопрос «зачем» — самый важный из тех вопросов, которые вы должны задавать себе. Не только глобально в отношении целей всего проекта, но и локально в отношении каждого кусочка функциональности. Если вы не можете коротко и понятно ответить на вопрос «зачем вот эта сущность нужна» — значит, она не нужна.
|
||||
Вопрос «зачем» — самый важный из тех вопросов, которые вы должны задавать себе. Не только глобально в отношении целей всего проекта, но и локально в отношении каждого кусочка функциональности. **Если вы не можете коротко и понятно ответить на вопрос «зачем эта сущность нужна» — значит, она не нужна**.
|
||||
|
||||
Здесь и далее предположим (в целях придания нашему примеру глубины и некоторой упоротости), что мы оптимизируем все три фактора в порядке убывания важности.
|
||||
|
||||
@ -165,7 +169,7 @@ API — _это обязательство_. Формальное обязате
|
||||
|
||||
1. Для решения задачи «заказать лунго» разработчику нужно обратиться к сущности «рецепт» и выяснить, что у каждого рецепта есть объём. Далее, нужно принять концепцию, что приготовление кофе заканчивается в тот момент, когда объём сравнялся с эталонным. Нет никакого способа об этой конвенции догадаться: она неочевидна и её нужно найти в документации. При этом никакой пользы для разработчика в этом знании нет.
|
||||
|
||||
2. Мы автоматически лишаем себя возможности варьировать объём кофе. Нам придётся или заводить ложные рецепты типа «большой лунго, средний лунго, маленький лунго», либо вводить ещё одну неявную конвенцию: если при создании заказа указать объём кофе, то готовность надо определять именно по нему. Обратите внимание, что тем самым при введении концепции объёма кофе в заказе разработчику так же надо будет изменить код функции, определяющей готовность кофе — а это уже совсем контринтуитивно, и обязательно приведёт к проблемам в будущем.
|
||||
2. Мы автоматически лишаем себя возможности варьировать объём кофе. Нам придётся или заводить ложные рецепты типа «большой лунго», «средний лунго», «маленький лунго», либо вводить ещё одну неявную конвенцию: если при создании заказа указать объём кофе, то готовность надо определять именно по нему. Обратите внимание, что тем самым при введении концепции объёма кофе в заказе разработчику так же надо будет изменить код функции, определяющей готовность кофе — а это уже совсем контринтуитивно, и обязательно приведёт к проблемам в будущем.
|
||||
|
||||
3. Вся эта схема полностью неработоспособна, если разные модели кофе-машин производят лунго разного объёма. Для решения задачи «объём лунго зависит от вида машины» нам придётся сделать совсем неприятную вещь: сделать рецепт зависимым от id машины. Тем самым мы начнём активно смешивать уровни абстракции: одной частью нашего API (рецептов) станет невозможно пользоваться без другой части (информации о кофе-машинах). Если разработчик хочет сделать какое-то абстрактное приложение про кофе (без его приготовления) — у него не получится это сделать.
|
||||
|
||||
@ -207,7 +211,7 @@ API — _это обязательство_. Формальное обязате
|
||||
|
||||
Дублирование функций на каждом уровне абстракций позволяет добиться важной вещи: возможности сменить нижележащие уровни без необходимости переписывать верхнеуровневый код. Мы можем добавить другие виды кофе-машин с принципиально другими физическими способами определения готовности напитка, и наш метод `GET /orders?order_id={id}` продолжит работать, как работал.
|
||||
|
||||
Да, код, который работал с физическим уровнем, придётся переписать. Но, во-первых, это неизбежно: изменение принципов работы физического уровня стэка автоматически означает необходимость переписать код. Во-вторых, такое разделение ставит перед нами четкий вопрос: до какого момента API должно предоставлять публичный доступ? Стоило ли предоставлять пользователю методы физического уровня? В-третьих, грамотное проектирование интерфейсов помогает избежать и этой проблемы, о чем будет подробнее рассказано ниже.
|
||||
Да, код, который работал с физическим уровнем, придётся переписать. Но, во-первых, это неизбежно: изменение принципов работы физического уровня автоматически означает необходимость переписать код. Во-вторых, такое разделение ставит перед нами четкий вопрос: до какого момента API должно предоставлять публичный доступ? Стоило ли предоставлять пользователю методы физического уровня? В-третьих, грамотное проектирование интерфейсов помогает избежать и этой проблемы, о чем будет подробнее рассказано ниже.
|
||||
|
||||
### Разграничение областей ответственности
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user