From 885d89cddb88217ed8c97ed78a746f116c336756 Mon Sep 17 00:00:00 2001 From: Sergey Konstantinov Date: Thu, 16 Mar 2023 00:26:41 +0200 Subject: [PATCH] UI started --- .../06-Раздел V. SDK и UI-библиотеки/02.md | 4 +- .../06-Раздел V. SDK и UI-библиотеки/04.md | 57 ++++++++++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/ru/drafts/06-Раздел V. SDK и UI-библиотеки/02.md b/src/ru/drafts/06-Раздел V. SDK и UI-библиотеки/02.md index fab7a02..94089b8 100644 --- a/src/ru/drafts/06-Раздел V. SDK и UI-библиотеки/02.md +++ b/src/ru/drafts/06-Раздел V. SDK и UI-библиотеки/02.md @@ -207,4 +207,6 @@ Как мы видим, простая операция «попробовать продлить предложение» выливается в громоздкий код, в котором легко ошибиться, и, что ещё важнее, который совершенно не нужен разработчику приложения. Было бы гораздо проще, если бы этой ошибки *вовсе не было в SDK*, т.е. попытки обновления и перезапросы выполнялись бы автоматически. - Аналогичные ситуации возникают и в случае нестрого-консистентных API или оптимистичного управления параллелизмом — и вообще в любом API, в котором фон ошибок является ожидаемым (что в в случае распределённых клиент-серверных API является нормой жизни). Передача версии ресурса и/или последних известных идентификаторов в эндпойнты с политикой read-your-writes — техническая необходимость, вызванная стремлением вендора API удешевить эксплуатацию и увеличить пропускную способность. Для разработчика приложения написание кода, имплементирующего эти политики — попросту напрасная трата времени. \ No newline at end of file + Аналогичные ситуации возникают и в случае нестрого-консистентных API или оптимистичного управления параллелизмом — и вообще в любом API, в котором фон ошибок является ожидаемым (что в в случае распределённых клиент-серверных API является нормой жизни). Передача версии ресурса и/или последних известных идентификаторов в эндпойнты с политикой read-your-writes — техническая необходимость, вызванная стремлением вендора API удешевить эксплуатацию и увеличить пропускную способность. Для разработчика приложения написание кода, имплементирующего эти политики — попросту напрасная трата времени. + + 5. Хранение данных в постоянной памяти (таких, как токены авторизации, ключи идемпотентности при перезапросах, идентификаторы черновиков при двухфазных коммитах и т.д.) также является ответственностью клиента и с трудом поддаётся формализации при кодогенерации. diff --git a/src/ru/drafts/06-Раздел V. SDK и UI-библиотеки/04.md b/src/ru/drafts/06-Раздел V. SDK и UI-библиотеки/04.md index 410c22e..d996776 100644 --- a/src/ru/drafts/06-Раздел V. SDK и UI-библиотеки/04.md +++ b/src/ru/drafts/06-Раздел V. SDK и UI-библиотеки/04.md @@ -1,3 +1,58 @@ ### UI-библиотеки -#### Особенности предметной области +Введение в состав SDK UI компонентов обогащает и так не самую простую конструкцию из клиент-серверного API и клиентской библиотеки дополнительным измерением: теперь с вашим API взаимодействуют одновременно и разработчики (которые написали код приложения), и пользователи (которые непосредственно тыкают пальцами в экран). Хотя это изменение на первый взгляд может показаться не очень значительным, с точки зрения дизайна API добавление конечного пользователя — огромная проблема, которая требует на порядок более глубокой и качественной проработки дизайна программных интерфейсов по сравнению с «чистым» клиент-серверным API. Попробуем объяснить, почему так происходит, на конкретном примере. + +Пусть мы решили поставлять в составе нашего кофейного API так же и клиентский SDK, который предоставляет готовые компоненты для разработчиков приложений. Достаточно простая функциональность: пользователь вводит поисковый запрос и видит результаты в виде списка либо в виде точек на карте. + +(здесь будет картинка) + +Пользователь может выбрать какой-либо из объектов, и тогда появится панель действий с выбранным предложением. + +(здесь будет картинка) + +Для реализации этого сценария мы предоставим объектно-ориентированный API в виде, ну скажем, класса `SearchBox`, который реализует описанную функциональность поверх клиент-серверного метода `search` нашего API. + +#### Проблемы + +С одной стороны нам может показаться, что наш `SearchBox.search` и чистый клиент-серверный `search` — в сущности одно и то же. Увы, это не так; перечислим проблемы, с которыми мы никогда не сталкивались при разработке API без визуальных компонент. + +##### Захват разделяемого ресурса + +Предположим, что мы хотим разрешить разработчику подставить в наш `SearchBox` свой поисковый запрос. Допустим, потому что где-то в приложении размещён рекламный баннер «найти лунго рядом со мной», по нажатию на который происходит переход к нашему компоненту с введённым запросом «лунго». Этот переход мы выполним, допустим, разработав метод `SeachBox.search`. + +Теперь у нас два метода `search`, которые принимают одни и те же параметры и выдают один и тот же результат. Но *ведут себя* эти методы совершенно по-разному: + * если вызвать несколько раз `SearchBox.search`, не дожидаясь ответа сервера, то все запросы, кроме последнего во времени, должны быть проигнорированы; даже если ответы пришли вразнобой, только тот из них, который соответствует новейшему запросу, должен быть показан в UI; + * дополнительная задача — каким должен быть результат операции `SearchBox.search`, если она была прервана выполнением следующего запроса? Если неуспех, то в чём состоит ошибка вызывающего? Если успех, то почему результат не был отражён в UI? + * что порождает другую проблему: а если в момент вызова `SearchBox.search` уже исполнялся какой-то запрос, инициированный пользователем — *что должно произойти*? Какой из вызовов приоритетнее — выполненный разработчиком или выполненный самим пользователем. + +В реализации клиент-серверного API такой проблемы у нас нет — каждый актор, вызывающий функцию поиска, получит свой ответ независимо. Но с UI-компонентами этот подход не работает, поскольку все они, в конечном итоге, разделяют один общий ресурс — экран приложения и внимание пользователя на нём. + +Любая асинхронная операция в UI-компонентах, особенно если она индицируется визуально (с помощью анимации или другого длящегося действия), может помешать любой другой визуальной операции — в том числе вследствие действий пользователя. + +##### Сильная связность бизнес-логики и отображения + +Посмотрим теперь на панель действий с предложениями. Допустим, мы размещаем на ней две кнопки — «заказать» и «показать на карте». Эти две кнопки выглядят одинаково и реагируют на действия пользователя одинаково — но при этом осуществляют абсолютно не имеющие ничего общего друг с другом действия. + +Допустим, мы предоставили программисту возможность модифицировать панель действий, для чего предоставим в составе SDK класс `Button`. Достаточно быстро мы выясним, что этой функциональностью будут пользоваться в двух основных диаметрально противоположных сценариях: + * для размещения на панели дополнительных кнопок, ну скажем, «позвонить в кафе», *выполненных в том же дизайне, что и стандартные*; + * для изменения дизайна стандартных кнопок в соответствии с фирменным стилем заказчика, *сохраняя ту же самую функциональность в неизменном виде*. + +Более того, возможен и третий сценарий: разработчики заходят сделать кнопку «позвонить», которая будет и выглядеть иначе, и выполнять другие действия, но при этом будет наследовать UX кнопки — т.е. нажиматься при клике, располагаться в линию с другими кнопками и так далее. + +С точки зрения разработчика SDK это означает, что функциональная кнопка должна позволять независимо переопределять и её внешний вид, и реакцию на действия пользователя. + +##### Двойная иерархия подчинения сущностей + +Предположим, что разработчик хочет обогатить дизайн списка предложений иконками сетей кофеен. Если изображение известно, оно должно быть показано всюду, где происходит работа с предложением конкретной кофейни. + +(здесь будет картинка) + +Теперь предположим, что разработчик также переопределил внешний вид всех кнопок в SDK, добавив иконки действий: + +(здесь будет картинка) + +Возникает вопрос: если выбрано предложение сетевой кофейни, какая иконка должна быть на кнопке подтверждения заказа — та, что унаследована из данных предложения (логотип кофейни) или та, что унаследована от «рода занятий» самой кнопки? Элемент управления «создать заказ», таким образом, встроен в две иерархии сущностей (по визуальному отображению и по данным) и в равной степени наследует обоим. + +#### И решения?… + +С решением вышеуказанных проблем, увы, всё обстоит очень сложно. Мы рассмотрим некоторые подходы по разделению областей ответственности составляющих в следующих главах; но очень важно уяснить одну важную мысль: полный декаплинг, то есть разработка функционального SDK+UI, дающего разработчику свободу в переопределении и внешнего вида, и реакции на действия, и UX — невероятно дорогая в разработке задача, которая в лучшем случае утроит вашу иерархию абстракций. Универсальный совет здесь ровно один: *три раза подумайте прежде чем предоставлять возможность программной настройки UI-компонентов*. Хотя цена ошибки дизайна программных интерфейсов, как правило, не очень высока для UI-библиотек, плохо структурированный, нечитабельный и глючный SDK вряд ли может рассматриваться как сильное клиентское преимущество вашего API. \ No newline at end of file