mirror of
https://github.com/twirl/The-API-Book.git
synced 2025-03-17 20:42:26 +02:00
Наброски глав 14-15
This commit is contained in:
parent
5600e0a8af
commit
3ff8809b7d
@ -1,14 +1,24 @@
|
||||
### Интерфейсы как универсальный паттерн
|
||||
|
||||
Как мы указали в предыдущей главе, основные причины внесения изменений в API — развитие самого API (добавление новой функциональности) и эволюция платформ (клиентской, серверной и предметной) — следует предусматривать ещё на этапе проектирования. Может показаться, что совет этот полезен примерно так же, как и сократовское определение человека — очень конкретен, и столь же бесполезен — но это не так. Методология, позволяющая получить устойчивое к изменениям API, существует и вполне конкретна: это применение концепции «интерфейса» ко всем уровням абстракции.
|
||||
|
||||
На практике это означает следующая: нам необходимо рассмотреть каждую сущность нашего API и выделить её абстрактную модель, т.е. разделить все поля и методы сущности на две группы: те, которые абсолютно необходимы для корректного цикла работы API, и те, которые мы можем назвать «деталями имплементации». Первые образуют *интерфейс* сущности: если заменить одну конкретную реализацию этого интерфейса на другую, API будет продолжать работать.
|
||||
|
||||
**NB**: мы понимаем, что вносим некоторую путаницу, поскольку термин «интерфейс» также используется для обозначения совокупности свойств и методов сущности, да и вообще отвечает за букву «I» в самой аббревиатуре «API»; однако использование других терминов внесёт ещё больше путаницы. Мы могли бы оперировать выражениями «абстрактные типы данных» и «контрактное программирование», но это методологически неверно: разработка API в принципе представляет собой контрактное программирование, при этом большинство клиент-серверных архитектур подразумевают независимость имплементации клиента и сервера, так что никаких «неабстрактных» типов данных в них не существует. Термины типа «виртуальный класс» и «виртуальное наследование» неприменимы по той же причине. Мы могли бы использовать «фасад», но под «фасадом» обычно понимают всё-таки конкретную имплементацию, а не абстракцию. Ближе всего по смыслу подходят «концепты» в том смысле, который вкладывается в них в STL, но «интерфейс» нам кажется более понятным.
|
||||
|
||||
Мы будем использовать термин «интерфейс» как обобщение понятия «абстрактный тип данных» и «контракт». «Интерфейс» — это некоторое абстрактное подмножество абстрактного типа данных, «метаконтракт». Интерфейсы мы будем обозначать с помощью префикса `I`, например: `Recipe` — это модель данных «рецепт», а `IRecipe` — это интерфейс рецепта: «минимальная» модель данных и операций над ними, которая достаточна для корректной работы API. Объект `Recipe` таким образом имплементирует интерфейс `IRecipe`.
|
||||
|
||||
Попробуем применить этот (дважды) абстрактный концепт к нашему кофейному API. Представьте, что на этапе разработки архитектуры бизнес выдвинул следующее требование: мы не только предоставляем доступ к оборудованию партнеров, но и предлагаем партнерам наше ПО (т.е. в данном случае API), чтобы они могли строить поверх него свои собственные приложения.
|
||||
|
||||
**NB**: в рассматриваемых нами примерах мы будем выстраивать интерфейсы так, чтобы связывание разных сущностей происходило динамически в реальном времени; в реальном мире такие интеграции будут делаться на стороне сервера путём написания ad hoc кода и формирования конкретных договорённостей с конкретным клиентом, однако мы для целей обучения специально будем идти более сложным и абстрактным путём. Динамическое связывание в реалтайме применимо скорее к сложным программным конструктам типа API операционных систем или встраиваемых библиотек; приводить обучающие примеры на основе систем такой сложности было бы затруднительно.
|
||||
|
||||
### API: вариационный анализ
|
||||
|
||||
В предыдущих разделах мы старались приводить теоретические правила и принципы, и иллюстрировать их на практических примерах. Однако понимание принципов проектирования API, устойчивого к изменениям, как ничто другое требует прежде всего практики. Знание о том, куда стоит «постелить соломку» — оно во многом «сын ошибок трудных». Нельзя предусмотреть всего — но можно выработать необходимый уровень технической интуиции.
|
||||
|
||||
Поэтому в этом разделе мы поступим следующим образом: возьмём наше модельное API из предыдущего раздела, и проверим его на устойчивость в каждой возможной точке — проведём некоторый «вариационный анализ» наших интерфейсов. Ещё более конкретно — к каждой сущности мы подойдём с вопросом «что, если?» — что, если нам потребуется предоставить партнерам возможность написать свою независимую реализацию этого фрагмента логики.
|
||||
|
||||
Первый важный момент, на который стоит обратить внимание: мы говорим именно о вариантах реализации _продуктовой логики_. Не о _вариантах реализации сущности_: изменения в API вносятся прежде всего для того, чтобы можно было сделать что-то, не предусмотренное изначальным дизайном — что-то полезное. Заниматься реимплементацией интерфейсов просто так ваши потребители не будут.
|
||||
|
||||
Это соображение вносит определённые ограничения, которые позволяют нам не заниматься варьированием интерфейсов вслепую (в конце концов, вариантов таких вариантов бесконечное количество, и предусматривать их все — сизифов труд): нам нужно понять в первую очередь _зачем_ нужны те или иные изменения, и отсюда мы уже поймём _как_ их следует внести.
|
||||
|
||||
Второй важный момент состоит в том, что многие решения, допускающие эту вариативность, _уже заложены_ в дизайне API. Какие-то из них (например, вопрос определения готовности) мы осветили в предыдущих главах подробнее, а какие-то дали без комментариев — настало время объяснить, почему эти решения были приняты.
|
||||
|
||||
**NB**: в рассматриваемых нами примерах мы будем выстраивать интерфейсы так, чтобы связывание разных сущностей происходило динамически в реальном времени; на практике такие интеграции будут делаться на стороне сервера путём написания ad hoc кода и формирования конкретных договорённостей с конкретным клиентом, однако мы для целей обучения специально будем идти более сложным и абстрактным путём. Динамическое связывание в реальном времени применимо скорее к сложным программным конструктам типа API операционных систем или встраиваемых библиотек; приводить обучающие примеры на основе систем подобной сложности было бы, однако, чересчур затруднительно.
|
||||
|
||||
#### Шаг 1. Собственные рецепты
|
||||
|
||||
Предположим, что мы решили предоставить партнёрам возможность готовить кофе по их собственным рецептам. Какова мотивация предоставления такой функциональности?
|
||||
|
||||
* возможно, партнерская сеть кофеен хочет предложить клиентам особенные «брендовые» напитки;
|
||||
* возможно, партнер хочет построить полностью своё приложение со своим ассортиментом на нашей платформе.
|
||||
|
||||
Разница между этими вариантами в том, что в первом случае брендированные напитки должны «подмешиваться» в общую поисковую выдачу; во втором случае поиск осуществляется только по рецептам партнера.
|
||||
|
||||
Что касается реализации
|
||||
|
15
src/ru/drafts/03-Раздел II. Обратная совместимость/04.md
Normal file
15
src/ru/drafts/03-Раздел II. Обратная совместимость/04.md
Normal file
@ -0,0 +1,15 @@
|
||||
### Интерфейсы как универсальный паттерн
|
||||
|
||||
Как мы указали в предыдущей главе, основные причины внесения изменений в API — развитие самого API (добавление новой функциональности) и эволюция платформ (клиентской, серверной и предметной) — следует предусматривать ещё на этапе проектирования. Может показаться, что совет этот полезен примерно так же, как и сократовское определение человека — очень конкретен, и столь же бесполезен — но это не так. Методология, позволяющая получить устойчивое к изменениям API, существует и вполне конкретна: это применение концепции «интерфейса» ко всем уровням абстракции.
|
||||
|
||||
На практике это означает следующая: нам необходимо рассмотреть каждую сущность нашего API и выделить её абстрактную модель, т.е. разделить все поля и методы сущности на две группы: те, которые абсолютно необходимы для корректного цикла работы API, и те, которые мы можем назвать «деталями имплементации». Первые образуют *интерфейс* сущности: если заменить одну конкретную реализацию этого интерфейса на другую, API будет продолжать работать.
|
||||
|
||||
**NB**: мы понимаем, что вносим некоторую путаницу, поскольку термин «интерфейс» также используется для обозначения совокупности свойств и методов сущности, да и вообще отвечает за букву «I» в самой аббревиатуре «API»; однако использование других терминов внесёт ещё больше путаницы. Мы могли бы оперировать выражениями «абстрактные типы данных» и «контрактное программирование», но это методологически неверно: разработка API в принципе представляет собой контрактное программирование, при этом большинство клиент-серверных архитектур подразумевают независимость имплементации клиента и сервера, так что никаких «неабстрактных» типов данных в них не существует. Термины типа «виртуальный класс» и «виртуальное наследование» неприменимы по той же причине. Мы могли бы использовать «фасад», но под «фасадом» обычно понимают всё-таки конкретную имплементацию, а не абстракцию. Ближе всего по смыслу подходят «концепты» в том смысле, который вкладывается в них в STL[[ref B. Stroustrup, A. Sutton. A Concept Design for the STL, p. 38]](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3351.pdf), но «интерфейс» нам кажется более понятным.
|
||||
|
||||
Мы будем использовать термин «интерфейс» как обобщение понятия «абстрактный тип данных» и «контракт». «Интерфейс» — это некоторое абстрактное подмножество абстрактного типа данных, «метаконтракт». Интерфейсы мы будем обозначать с помощью префикса `I`, например: `Recipe` — это модель данных «рецепт», а `IRecipe` — это интерфейс рецепта: «минимальная» модель данных и операций над ними, которая достаточна для корректной работы API. Объект `Recipe` таким образом имплементирует интерфейс `IRecipe`.
|
||||
|
||||
Попробуем применить этот (дважды) абстрактный концепт к нашему кофейному API. Представьте, что на этапе разработки архитектуры бизнес выдвинул следующее требование: мы не только предоставляем доступ к оборудованию партнеров, но и предлагаем партнерам наше ПО (т.е. в данном случае API), чтобы они могли строить поверх него свои собственные приложения. Иными словами, подойдём к каждой концепции нашего API с вопросом «что, если?…»
|
||||
|
||||
**NB**: в рассматриваемых нами примерах мы будем выстраивать интерфейсы так, чтобы связывание разных сущностей происходило динамически в реальном времени; в реальном мире такие интеграции будут делаться на стороне сервера путём написания ad hoc кода и формирования конкретных договорённостей с конкретным клиентом, однако мы для целей обучения специально будем идти более сложным и абстрактным путём. Динамическое связывание в реалтайме применимо скорее к сложным программным конструктам типа API операционных систем или встраиваемых библиотек; приводить обучающие примеры на основе систем такой сложности было бы затруднительно.
|
||||
|
||||
**Что произойдёт, если…** потребуется предоставить партнёру возможность готовить напитки по своему собственному рецепту?
|
Loading…
x
Reference in New Issue
Block a user