mirror of
https://github.com/twirl/The-API-Book.git
synced 2025-01-05 10:20:22 +02:00
Опечтки
This commit is contained in:
parent
9edb3ff27d
commit
ac345ebb06
@ -34,7 +34,7 @@ function getParts ({ path, l10n: { chapter }}) {
|
||||
.sort()
|
||||
.forEach((file) => {
|
||||
const md = fs.readFileSync(`${subdir}${file}`, 'utf-8');
|
||||
parts.push(md.trim().replace(/$### /, `### ${chapter} ${counter++}. `));
|
||||
parts.push(md.trim().replace(/^### /, `### ${chapter} ${counter++}. `));
|
||||
});
|
||||
});
|
||||
|
||||
|
38
dist/API.ru.html
vendored
38
dist/API.ru.html
vendored
@ -50,7 +50,7 @@ h2 {
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<article id="percollate-page-5065e100-203d-11eb-bef9-4551dbb4656b" class="article">
|
||||
<article id="percollate-page-935ba4d0-203e-11eb-aa52-03023e30e35a" class="article">
|
||||
<header class="article__header">
|
||||
<h1 class="article__title">
|
||||
Сергей Константинов. API
|
||||
@ -68,14 +68,14 @@ h2 {
|
||||
<p><a href="http://creativecommons.org/licenses/by-nc/4.0/"></a></p><figure><a href="http://creativecommons.org/licenses/by-nc/4.0/"><img alt="Лицензия Creative Commons" src="https://i.creativecommons.org/l/by-nc/4.0/88x31.png"><figcaption>Лицензия Creative Commons</figcaption></a></figure><br>
|
||||
Это произведение доступно по <a href="http://creativecommons.org/licenses/by-nc/4.0/">лицензии Creative Commons «Attribution-NonCommercial» («Атрибуция — Некоммерческое использование») 4.0 Всемирная</a>.<p></p>
|
||||
<h2 id="-1">Введение</h2>
|
||||
<h3 id="-2">О структуре этой книги</h3>
|
||||
<h3 id="1">Глава 1. О структуре этой книги</h3>
|
||||
<p>Книга, которую вы держите в руках, состоит из введения и трех больших разделов.</p>
|
||||
<p>В первом разделе мы поговорим о проектировании API на стадии разработки концепции - как грамотно выстроить архитектуру, от крупноблочного планирования до конечных интерфейсов.</p>
|
||||
<p>Второй раздел будет посвящён жизненному циклу API - как интерфейсы эволюционируют со временем и как развивать продукт так, чтобы отвечать потребностям пользователей.</p>
|
||||
<p>Наконец, третий раздел будет касаться больше не-разработческих сторон жизни API - поддержки, маркетинга, работы с комьюнити.</p>
|
||||
<p>Первые два будут интересны скорее разработчикам, третий — и разработчикам, и менеджерам. При этом мы настаиваем, что как раз третий раздел — самый важный для разработчика API. Ввиду того, что API - продукт для разработчиков, перекладывать ответственность за его развитие и поддержку на не-разработчиков неправильно: никто кроме вас самих не понимает так хорошо продуктовые свойства вашего API.</p>
|
||||
<p>На этом переходим к делу.</p>
|
||||
<h3 id="api-1">Определение API</h3>
|
||||
<h3 id="2api">Глава 2. Определение API</h3>
|
||||
<p>Прежде чем говорить о разработке API, необходимо для начала договориться о том, что же такое API. Энциклопедия скажет нам, что API — это программный интерфейс приложений. Это точное определение, но бессмысленное. Примерно как определение человека по Платону: «двуногое без перьев» — определение точное, но никоим образом не дающее нам представление о том, чем на самом деле человек примечателен. (Да и не очень-то и точное: Диоген Синопский как-то ощипал петуха и заявил, что это человек Платона; пришлось дополнить определение уточнением «с плоскими ногтями».)</p>
|
||||
<p>Что же такое API по смыслу, а не по формальному определению?</p>
|
||||
<p>Вероятно, вы сейчас читаете эту книгу посредством браузера. Чтобы браузер смог отобразить эту страничку, должны корректно отработать: разбор URL согласно спецификации; служба DNS; соединение по протоколу TLS; передача данных по протоколу HTTP; разбор HTML-документа; разбор CSS-документа; корректный рендеринг HTML+CSS.</p>
|
||||
@ -90,7 +90,7 @@ h2 {
|
||||
<p>Отличие римского виадука от хорошего API состоит лишь в том, что API предлагает <em>программный</em> контракт. Для связывания двух областей необходимо написать некоторый <em>код</em>. Цель этой книги — помочь вам разработать API, так же хорошо выполняющий свою задачу, как и римский виадук.</p>
|
||||
<p>Виадук также хорошо иллюстрирует другую проблему разработки API: вашими пользователями являются инженеры. Вы не поставляете воду напрямую потребителю: к вашей инженерной мысли подключаются заказчики путём пристройки к ней каких-то своих инженерных конструкций. С одной стороны, вы можете обеспечить водой гораздо больше людей, нежели если бы вы сами подводили трубы к каждому крану. С другой — качество инженерных решений заказчика вы не может контролировать, и проблемы с водой, вызванные некомпетентностью подрядчика, неизбежно будут валить на вас.</p>
|
||||
<p>Поэтому проектирование API налагает на вас несколько большую ответственность. <strong>API является как мультипликатором ваших возможностей, так и мультипликатором ваших ошибок</strong>.</p>
|
||||
<h3 id="api-2">Критерии качества API</h3>
|
||||
<h3 id="3api">Глава 3. Критерии качества API</h3>
|
||||
<p>Прежде чем излагать рекомендации, нам следует определиться с тем, что мы считаем «хорошим» API, и какую пользу мы получаем от того, что наше API «хорошее».</p>
|
||||
<p>Начнём со второго вопроса. Очевидно, «хорошесть» API определяется в первую очередь тем, насколько он помогает разработчикам решать стоящие перед ними задачи. (Можно резонно возразить, что решение задач, стоящих перед разработчиками, не обязательно влечёт за собой выполнение целей, которые мы ставим перед собой, предлагая разработчикам API. Однако манипуляция общественным мнением не входит в область интересов автора этой книги: здесь и далее предполагается, что API существует в первую очередь для того, чтобы разработчики решали с его помощью свои задачи, а не ради каких-то не декларируемых явно целей.)</p>
|
||||
<p>Как же дизайн API может помочь разработчику? Очень просто: API должно <em>решать задачи</em>, и делать это максимально удобно и понятно. Путь разработчика от формулирования своей задачи до написания работающего кода должен быть максимально коротким. Это, в том числе, означает, что:</p>
|
||||
@ -102,14 +102,14 @@ h2 {
|
||||
<p>Однако статическое удобство и понятность API — это простая часть. В конце концов, никто не стремится специально сделать API нелогичным и нечитаемым — всегда при разработке мы начинаем с каких-то понятных базовых концепций. При минимальном опыте проектирования сложно сделать ядро API, не удовлетворяющее критериям очевидности, читаемости и консистентности.</p>
|
||||
<p>Проблемы начинаются, когда мы начинаем API развивать. Добавление новой фунциональности рано или поздно приводит к тому, что некогда простое и понятное API становится наслоением разных концепций, а попытки сохранить обратную совместимость приводят к нелогичным, неочевидным и попросту плохим решениям. Отчасти это связано так же и с тем, что невозможно обладать полным знанием о будущем: ваше понимание о «правильном» API тоже будет меняться со временем, как в объективной части (какие задачи решает API и как лучше это сделать), так и в субъективной — что такое очевидность, читабельность и консистентность для вашего API.</p>
|
||||
<p>Принципы, которые я буду излагать ниже, во многом ориентированы именно на то, чтобы API правильно развивалось во времени и не превращалось в нагромождение разнородных неконсистентных интерфейсов. Важно понимать, что такой подход тоже небесплатен: необходимость держать в голове варианты развития событий и закладывать возможность изменений в API означает избыточность интерфейсов и возможно излишнее абстрагирование. И то, и другое, помимо прочего, усложняет и работу программиста, пользующегося вашим API. <strong>Закладывание перспектив «на будущее» имеет смысл, только если это будущее у API есть, иначе это попросту оверинжиниринг</strong>.</p>
|
||||
<h3 id="-3">Обратная совместимость</h3>
|
||||
<h3 id="4">Глава 4. Обратная совместимость</h3>
|
||||
<p>Обратная совместимость — это некоторая <em>временна́я</em> характеристика качества вашего API. Именно необходимость поддержания обратной совместимости отличает разработку API от разработки программного обеспечения вообще.</p>
|
||||
<p>Разумеется, обратная совместимость не абсолютна. В некоторых предметных областях выпуск новых обратно несовместимых версий API является вполне рутинной процедурой. Тем не менее, каждый раз, когда выпускается новая обратно несовместимая версия API, всем разработчикам приходится инвестировать какое-то ненулевое количество усилий, чтобы адаптировать свой код к новой версии. В этом плане выпуск новых версий API является некоторого рода «налогом» на потребителей — им нужно тратить вполне осязаемые деньги только для того, чтобы их продукт продолжал работать.</p>
|
||||
<p>Конечно, крупные компании с прочным положением на рынке могут позволить себе такой налог взымать. Более того, они могут вводить какие-то санкции за отказ от перехода на новые версии API, вплоть до отключения приложений.</p>
|
||||
<p>С нашей точки зрения, подобное поведение ничем не может быть оправдано. Избегайте скрытых налогов на своих пользователей. Если вы можете не ломать обратную совсемстимость — не ломайте её.</p>
|
||||
<p>Да, безусловно, поддержка старых версий API — это тоже своего рода налог. Технологии меняются, и, как бы хорошо ни было спроектировано ваше API, всего предусмотреть невозможно. В какой-то момент ценой поддержки старых версий становится невозможность предоставлять новую функциональность и поддерживать новые платформы, и выпустить новую версию всё равно придётся. Однако вы по крайней мере сможете убедить своих потребителей в необходимости перехода.</p>
|
||||
<p>Более подробно о политиках версионирования будет рассказано в разделе II.</p>
|
||||
<h3 id="-4">О версионировании</h3>
|
||||
<h3 id="5">Глава 5. О версионировании</h3>
|
||||
<p>Здесь и далее мы будем придерживаться принципов версионирования <a href="https://semver.org/">semver</a>:</p>
|
||||
<ol>
|
||||
<li>Версия API задаётся тремя цифрами, вида <code>1.2.3</code></li>
|
||||
@ -118,12 +118,12 @@ h2 {
|
||||
<li>Третья цифра (патч) увеличивается при выпуске новых версий, содержащих только исправление ошибок</li>
|
||||
</ol>
|
||||
<p>Выражения «мажорная версия API» и «версия API, содержащая обратно несовместимые изменения функциональности» тем самым следует считать эквивалентными.</p>
|
||||
<h3 id="-5">Условные обозначения и терминология</h3>
|
||||
<h3 id="6">Глава 6. Условные обозначения и терминология</h3>
|
||||
<p>Разработка программного обеспечения характеризуется, помимо прочего, существованием множества различных парадигм разработки, адепты которых зачастую настроены весьма воинственно по отношению к адептам других парадигм. Поэтому при написании этой книги мы намеренно избегаем слов «метод», «объект», «функция» и так далее, используя нейтральный термин «сущность». Под «сущностью» понимается некоторая атомарная единица функциональности — класс, метод, объект, монада, прототип (нужное подчеркнуть).</p>
|
||||
<p>Для составных частей сущности, к сожалению, достаточно нейтрального термина нам придумать не удалось, поэтому мы используем слова «поля» и «методы».</p>
|
||||
<p>Большинство примеров API в общих разделах будут даны в виде JSON-over-HTTP-эндпойтов. Это некоторая условность, которая помогает описать концепции, как нам кажется, максимально понятно. Вместо <code>GET /orders</code> вполне может быть вызов метода <code>orders.get()</code>, локальный или удалённый; вместо JSON может быть любой другой формат данных. Смысл утверждений от этого не меняется.</p>
|
||||
<h2 id="iapi">I. Проектирование API</h2>
|
||||
<h3 id="api-3">Пирамида контекстов API</h3>
|
||||
<h3 id="7api">Глава 7. Пирамида контекстов API</h3>
|
||||
<p>Подход, который мы используем для проектирования, состоит из четырёх шагов:</p>
|
||||
<ul>
|
||||
<li>определение области применения;</li>
|
||||
@ -134,7 +134,7 @@ h2 {
|
||||
<p>Этот алгоритм строит API сверху вниз, от общих требований и сценариев использования до конкретной номенклатуры сущностей; фактически, двигаясь этим путем, вы получите на выходе готовое API — чем этот подход и ценен.</p>
|
||||
<p>Может показаться, что наиболее полезные советы и best practice приведены в последнем разделе, однако это не так; цена ошибки, допущенной на разных уровнях весьма различна. Если исправить плохое именование довольно просто, то исправить неверное понимание того, зачем вообще нужно API, практически невозможно.</p>
|
||||
<p><em>NB</em>. Здесь и далее мы будем рассматривать концепции разработки API на примере некоторого гипотетического API заказа кофе в городских кофейнях. На всякий случай сразу уточним, что пример является синтетическим; в реальной ситуации, если бы такое API пришлось проектировать, оно вероятно было бы совсем не похоже на наш выдуманный пример.</p>
|
||||
<h3 id="-6">Определение области применения</h3>
|
||||
<h3 id="8">Глава 8. Определение области применения</h3>
|
||||
<p>Ключевой вопрос, который вы должны задать себе четыре раза, выглядит так: какую проблему мы решаем? Задать его следует четыре раза с ударением на каждом из четырёх слов.</p>
|
||||
<ol>
|
||||
<li><p><em>Какую</em> проблему мы решаем? Можем ли мы чётко описать, в какой ситуации гипотетическим потребителям-разработчикам нужно наше API?</p></li>
|
||||
@ -156,12 +156,12 @@ h2 {
|
||||
<li><p>Наконец, правда ли мы решим проблему? Как мы поймём, что оптимизировали перечисленные факторы?</p></li>
|
||||
</ol>
|
||||
<p>На все эти вопросы, в общем случае, простого ответа нет. В идеале ответы на эти вопросы должны даваться с цифрами в руках. Сколько конкретно времени тратится неоптимально, и какого значения мы рассчитываем добиться, располагая какой плотностью кофемашин? Заметим также, что в реальной жизни просчитать такого рода цифры можно в основном для проектов, которые пытаются влезть на уже устоявшийся рынок; если вы пытаетесь сделать что-то новое, то, вероятно, вам придётся ориентироваться в основном на свою интуицию.</p>
|
||||
<h4 id="api-4">Почему API?</h4>
|
||||
<h4 id="api-1">Почему API?</h4>
|
||||
<p>Т.к. наша книга посвящена не просто разработке программного обеспечения, а разработке API, то на все эти вопросы мы должны взглянуть под другим ракурсом: а почему для решения этих задач требуется именно API, а не просто программное обеспечение? В нашем вымышленном примере мы должны спросить себя: зачем нам нужно предоставлять сервис для других разработчиков, чтобы они могли готовить кофе своим клиентам, а не сделать своё приложение для конечного потребителя?</p>
|
||||
<p>Иными словами, должна иметься веская причина, по которой два домена разработки ПО должны быть разделены: есть оператор(ы), предоставляющий API; есть оператор(ы), предоставляющий сервисы пользователям. Их интересы в чем-то различны настолько, что объединение этих двух ролей в одном лице нежелательно. Более подробно мы изложим причины и мотивации делать именно API в разделе II.</p>
|
||||
<p>Заметим также следующее: вы должны браться делать API тогда и только тогда, когда в ответе на второй вопрос написали «потому что в этом состоит наша экспертиза». Разрабатывая API вы занимаетесь некоторой мета-разработкой: вы разрабатывать ПО для того, чтобы другие могли разрабатывать ПО для решения задачи пользователя. Не обладая экспертизой в обоих этих доменах (API и конечные продукты) написать хорошее API сложно.</p>
|
||||
<p>Для нашего умозрительного примера предположим, что в недалеком будущем произошло разделение рынка кофе на две группы игроков: одни предоставляют само железо, кофейные аппараты, а другие имеют доступ к потребителю — примерно как это произошло, например, с рынком авиабилетов, где есть собственно авиакомпании, осуществляющие перевозку, и сервисы планирования путешествий, где люди выбирают варианты перелётов. Мы хотим агрегировать доступ к железу, чтобы владельцы приложений могли встраивать заказ кофе.</p>
|
||||
<h4 id="-7">Что и как</h4>
|
||||
<h4 id="-2">Что и как</h4>
|
||||
<p>Закончив со всеми теоретическими упражнениями, мы должны перейти непосредственно к дизайну и разработки API, имея понимание по двум пунктам:</p>
|
||||
<ol>
|
||||
<li>Что конкретно мы делаем</li>
|
||||
@ -173,7 +173,7 @@ h2 {
|
||||
<li>Для этого мы абстрагируем за нашим HTTP API доступ к «железу» и предоставим методы для выбора вида напитка и места его приготовления и для непосредственно исполнения заказа.</li>
|
||||
</ol>
|
||||
<p>С этими вводными мы можем переходить непосредственно к разработке.</p>
|
||||
<h3 id="-8">Разделение уровней абстракции</h3>
|
||||
<h3 id="9">Глава 9. Разделение уровней абстракции</h3>
|
||||
<p>«Разделите свой код на уровни абстракции» - пожалуй, самый общий совет для разработчиков программного обеспечения. Однако будет вовсе не преувеличением сказать, что изоляция уровней абстрации — самая сложная задача, стоящая перед разработчиком API.</p>
|
||||
<p>Прежде чем переходить к теории, следует чётко сформулировать, <em>зачем</em> нужны уровни абстракции и каких целей мы хотим достичь их выделением.</p>
|
||||
<p>Вспомним, что программный продукт - это средство связи контекстов, средство преобразования терминов и операций одной предметной области в другую. Чем дальше друг от друга эти области отстоят - тем большее число промежуточных передаточных звеньев нам придётся ввести. Вернёмся к нашему примеру с кофейнями. Какие уровни сущностей мы видим?</p>
|
||||
@ -259,7 +259,7 @@ h2 {
|
||||
</ul>
|
||||
<p>На это (совершенно верное!) замечаниемы ответим, что выделение уровней абстракции — прежде всего <em>логическая</em> процедура: как мы объясняем себе и разработчику, из чего состоит наш API. Мы могли бы просто ограничиться выделением секции <code>task</code> в ответе <code>GET /orders/{id}</code> — или вовсе сказать, что <code>task</code> — это просто четверка полей (<code>ready</code>, <code>volume_requested</code>, <code>volume_prepared</code>, <code>readiness_policy</code>) и есть. <strong>Абстрагируемая дистанция между сущностями существует объективно</strong>, каким бы образом мы ни написали конкретные интерфейсы. Наша задача состоит только лишь в том, чтобы эта дистанция была разделена на уровни <em>явно</em>. Чем неявнее разведены уровни абстракции (или хуже того, перемешаны) уровни абстракции, тем сложнее будет разобраться в вашем API и тем хуже будет написан использующий его код.</p>
|
||||
<p>NB: важно заметить, что с дальнейшей проработкой уровень исполнения, скорее всего, сам должен будет разделиться на два и более уровня, т.к. «задача» по сути — просто сущность-зонтик, связывающая в рамках заказа несколько высокоуровневых сущностей. Идея определения параметров кофе-машины на этапе создания заказов не очень удобна, да и до манипуляции командами кофе-машины и состоянием сенсоров всё ещё далеко с точки зрения абстрагирования. Но мы пока оставим в таком виде, для удобства дальнейшего изложения.</p>
|
||||
<h4 id="-9">Изоляция уровней абстракции</h4>
|
||||
<h4 id="-3">Изоляция уровней абстракции</h4>
|
||||
<p>Важное свойство правильно подобранных уровней абстракции, и отсюда требование к их проектированию — это требование изоляции: <strong>взамодействие возможно только между сущностями соседних уровней абстракции</strong>. Если при проектировании выясняется, что для выполнения того или иного действия требуется «перепрыгнуть» уровень абстракции, это явный признак того, что в проекте допущены ошибки.</p>
|
||||
<p>Возвращаясь к нашему примеру с готовностью кофе: проблемы с определением готовности кофе исходя из объёма возникают именно потому, что мы не можем ожидать от пользователя, создающего заказ, знания о необходимости проверки объёма налитого реальной кофе-машиной объёма кофе. Мы вводим дополнительный уровень абстракции именно для того, чтобы на нём переформулировать, что такое «заказ готов».</p>
|
||||
<p>Важным следствием этого принципа является то, что информацию о готовности заказа нам придётся «прорастить» через все уровни абстракции:</p>
|
||||
@ -290,7 +290,7 @@ h2 {
|
||||
<p>Может показаться, что соблюдение правила изоляции уровней абстракции является избыточным и заставляет усложнять интерфейс. И это в действительности так: важно понимать, что никакая гибкость, логичность, читабельность и расширяемость не бывает бесплатной. Можно построить API так, чтобы оно выполняло свою функцию с минимальными накладными расходами, по сути — дать интерфейс к микроконтроллерам кофе-машины. Однако пользоваться им будет крайне неудобно, и расширяемость такого API будет нулевой.</p>
|
||||
<p>Дублирование функций на каждом уровне абстракций позволяет добиться важной вещи: возможности сменить нижележащие уровни без необходимости переписывать верхнеуровневый код. Мы можем добавить другие виды кофе-машин с принципиально другими физическими способами определения готовности напитка, и наш метод <code>GET /orders?order_id={id}</code> продолжит работать, как работал.</p>
|
||||
<p>Да, код, который работал с физическим уровнем, придётся переписать. Но, во-первых, это неизбежно: изменение принципов работы физического уровня автоматически означает необходимость переписать код. Во-вторых, такое разделение ставит перед нами четкий вопрос: до какого момента API должно предоставлять публичный доступ? Стоило ли предоставлять пользователю методы физического уровня?</p>
|
||||
<h3 id="-10">Разграничение областей ответственности</h3>
|
||||
<h3 id="10">Глава 10. Разграничение областей ответственности</h3>
|
||||
<p>Исходя из описанного в предыдущей главе, мы понимаем, что иерархия абстракций в нашем гипотетическом проекте должна выглядеть примерно так:</p>
|
||||
<ul>
|
||||
<li>пользовательский уровень (те сущности, с которыми непосредственно взаимодействует пользователь и сформулированы в понятных для него терминах; например, заказы и виды кофе);</li>
|
||||
@ -312,12 +312,12 @@ h2 {
|
||||
<li>отправлять команды и проверять их исполнение</li></ul></li>
|
||||
</ol>
|
||||
<p>Если внимательно посмотреть на каждый объект, то мы увидим, что, в итоге, каждый объект оказался в смысле своей ответственности составным. Например, <code>coffee-machine</code> будет частично оперировать реальными командами кофе-машины <em>и</em> представлять их состояние в каком-то машиночитаемом виде.</p>
|
||||
<h4 id="-11">Декомпозиция интерфейсов</h4>
|
||||
<h4 id="-4">Декомпозиция интерфейсов</h4>
|
||||
<p>// TODO</p>
|
||||
<p>NB. Во многих языках программирования нет поддержки интерфейсов, абстрактных классов и/или множественного наследования. Однако выделять интерфейсы нам это не мешает, поскольку мы всегда можем "договориться", что объект source имеет право пользоваться только вот этим набором свойств и методов. Конечно, контролировать соблюдение этой договоренности в достаточно развесистом API довольно сложно, но, поверьте автору, вполне возможно, тем более, что тестами и/или статическим анализом кода соблюдение договоренностей об интерфейсах можно проверить почти всегда.</p>
|
||||
<p>Разделение контекстов - не единственная причина, по которой выделение интерфейсов критически важно при проектировании API. Предъявление к входящим параметрам требования только удовлетворять интерфейсу существенно упрощает создание альтернативных реализаций ваших объектов, в том числе в целях тестирования. Теперь чтобы протестировать объект source достаточно написать mock на IGeoContext, а не весь класс map целиком. Аналогично, если мы захотим использовать наши оверлеи для показа их, скажем, в качестве какой-то инфографики или на абстрактном плане местности, нам не придётся переделывать для этого класс Map - достаточно будет альтернативной реализации IGraphicalContext.</p>
|
||||
<p>При выделении интерфейсов важно также понимать, что интерфейс, в отличие от его реализации, должен быть минимально достаточным и не должен включать в себя вспомогательные методы. Например, если класс map имеет как метод для получения всей области картографирования в виде четырехугольника getBBox, так и методы получения каждого из углов по отдельности - getLeftBottom, getRightTop, например, — то интерфейс IGeoContext должен содержать что-то одно. Нет никакого смысла загромождать интерфейс альтернативными реализациями одной и той же функциональности — это затрудняет чтение и усложняет написание собственных реализаций. Если только нет каких-то показаний с точки зрения производительности, следует отдать предпочтение максимально общему методу - в нашем случае getBBox.</p>
|
||||
<h4 id="-12">Интерфейсы как универсальный паттерн</h4>
|
||||
<h4 id="-5">Интерфейсы как универсальный паттерн</h4>
|
||||
<p>Как мы убедились в предыдущей главе, выделение интерфейсов крайне важно с точки зрения удобства написания кода. Однако, интерфейсы играют и другую важную роль в проектировании: они позволяют уложить в голове архитектуру API целиком.</p>
|
||||
<p>Любой сколько-нибудь крупный API рано или поздно обрастает разнообразной номенклатурой сущностей, их свойст и методов, как в силу того, что в одном объекте «сходятся» несколько предметных областей, так и в силу появления со временем разнообразной вспомогательной и дополнительной функциональности. Особенно сложной номенклатура объектов и их методов становится в случае появления альтернативных реализаций одного и того же интерфейса.</p>
|
||||
<p>Человеческие возможности небезграничны: невозможно держать в голове всю номенклатуру объектов. Это осложняет и проектирование API, и рефакторинг, и просто решение возникающих задач по реализации той или иной бизнес-логики.</p>
|
||||
@ -327,7 +327,7 @@ h2 {
|
||||
<p>С другой стороны надо вновь напомнить, что бесплатно ничего не бывает, и выделение интерфейсов - самая «небесплатная» часть процесса разработки API, поскольку в чистом виде приносится в жертву удобство разработки ради построения «правильной» архитектуры: разумеется, код писать куда проще, когда имеешь доступ ко всем объектам API со всей их богатой номенклатурой методов, нежели когда из каждого объекта доступны только пара непосредственно примыкающих интерфейсов, притом с максимально общими методами.</p>
|
||||
<p>Помимо прочего, это означает, что интерфейсы необходимо выделять там, где это актуально решаемой задаче - прежде всего, в точках будущего роста и там, где возможны альтернативные реализации. Чем проще API, тем меньше нужда в интерфейсах, и наоборот: сложное API требует интерфейсов практически всюду просто для того, чтобы ограничить разрастание излишне сильной связанности и при этом не сойти с ума.</p>
|
||||
<p>В пределе в сложном api должна сложиться ситуация, при которой все объекты взаимодействуют друг с другом только как интерфейсы — нет ни одной публичной сигнатуры, принимающей конкретный объект, а не его интерфейс. Разумеется, достичь такого уровня абстракции практически невозможно - почти в любой системе есть глобальные объекты, разнообразные технические сущности (имплементации стандартных структур данных, например); наконец, невозможно «спрятать» за интерфейсы системные объекты или сущности физического уровня.</p>
|
||||
<h4 id="-13">Информационные контексты</h4>
|
||||
<h4 id="-6">Информационные контексты</h4>
|
||||
<p>При выделении интерфейсов и вообще при проектировании api бывает полезно взглянуть на иерархию абстракций с другой точки зрения, а именно: каким образом информация «протекает» через нашу иерархию.</p>
|
||||
<p>Вспомним, что одним из критериев отделения уровней абстракции является переход от структур данных одной предметной области к структурам данных другой. В рамках нашего примера через иерархию наших объектов происходит трансляция данных реального мира — «железные» кофе-машины, которые готовят реально существующие напитки — в виртуальные интерфейсы «заказов».</p>
|
||||
<p>Если обратиться к правилу «неперепрыгивания» через уровни абстракции, то с точки зрения потоков данных оно формулируется так:</p>
|
||||
@ -336,7 +336,7 @@ h2 {
|
||||
<li>преобразованием данных имеют право заниматься только те объекты, в чьи непосредственные обязанности это входит.</li>
|
||||
</ul>
|
||||
<p>Дерево информационных контекстов (какой объект обладает какой информацией, и кто является транслятором из одного контекста в другой), по сути, представляет собой «срез» нашего дерева иерархии интерфейсов; выделение такого среза позволяет проще и удобнее удерживать в голове всю архитектуру проекта.</p>
|
||||
<h3 id="-14">Описание конечных интерфейсов</h3>
|
||||
<h3 id="11">Глава 11. Описание конечных интерфейсов</h3>
|
||||
<p>Определив все сущности, их ответственность и отношения друг с другом, мы переходим непосредственно к разработке API: нам осталось прописать номенклатуру всех объектов, полей, методов и функций в деталях. В этой главе мы дадим сугубо практические советы, как сделать API удобным и понятным.</p>
|
||||
<p>Важное уточнение под номером ноль:</p>
|
||||
<ol start="0">
|
||||
@ -375,7 +375,7 @@ h2 {
|
||||
<li><p>Аналогично, если ожидается булево значение, то из названия это должно быть очевидно, т.е. именование должно описывать некоторое качественное состояние, например, <code>is_ready</code>, <code>open_now</code>:</p>
|
||||
<ul>
|
||||
<li>плохо: <code>"task.status": true</code>
|
||||
— неочевидно, что статус бинарен, плюс такое API будет нерасширяемым
|
||||
— неочевидно, что статус бинарен, плюс такое API будет нерасширяемым<br>
|
||||
хорошо: <code>"task.is_finished": true</code></li></ul></li>
|
||||
<li><p>Сущности, выполняющие подобные функции, должны называться подобно и вести себя подобным образом.</p>
|
||||
<ul>
|
||||
|
BIN
dist/API.ru.pdf
vendored
BIN
dist/API.ru.pdf
vendored
Binary file not shown.
@ -38,7 +38,7 @@
|
||||
|
||||
5. Аналогично, если ожидается булево значение, то из названия это должно быть очевидно, т.е. именование должно описывать некоторое качественное состояние, например, `is_ready`, `open_now`:
|
||||
* плохо: `"task.status": true`
|
||||
— неочевидно, что статус бинарен, плюс такое API будет нерасширяемым
|
||||
— неочевидно, что статус бинарен, плюс такое API будет нерасширяемым
|
||||
хорошо: `"task.is_finished": true`
|
||||
|
||||
6. Сущности, выполняющие подобные функции, должны называться подобно и вести себя подобным образом.
|
||||
|
Loading…
Reference in New Issue
Block a user