Легко невозможный дизайн API | Кодементор

Создавать графические пользовательские интерфейсы сложно.

Одна из причин — плохой дизайн API. Особенно плохой баланс контроль против усилие над набором вариантов использования. Усилия — это время, необходимое для реализации варианта использования с использованием API. Контроль — это то, насколько детализированными могут быть команды, которые мы отправляем в API.

Эта проблема баланса не связана с графическим интерфейсом, это общая проблема в дизайне API. Этот пост — моя попытка понять динамику между контролем и усилиями на примерах из фронтенд-разработки.

Утраченное искусство геометрии

Вот головоломка. Допустим, у нас есть два прямоугольника. Один выше и шире другого. Мы хотим разместить и вертикально центрировать меньший прямоугольник внутри большего.

Высота большего прямоугольника равна h1. Высота меньшего равна h2. Как мы решаем для xкуда x вертикальное смещение одного прямоугольника относительно другого?

ex1.png

Если вы хотите попробовать, вот несколько вариантов:

1. x = h1 / 2
2. x = (h1 - h2) / 2
3. x = (h1 + h2) / 2

Посмотреть правильный ответ

Видите ли, это всего лишь одна строчка математики — этого старого непривлекательного языка программирования электронных таблиц.

Если мы перенесем эту проблему из мира форм в мир HTML-документов, она все еще решаема. На этот раз с CSS — новым крутым языком стилей браузеров.

.container {
  display: flex;
  align-items: center;
}

Немного более сложная головоломка

Давайте сделаем это немного сложнее. Теперь нам нужно привязать верхнюю сторону меньшего прямоугольника к сетке. В остальном то же самое. Мы по-прежнему центрируем меньший прямоугольник внутри большего, только на этот раз округляя значение x к ближайшему ряду сетки.

Высота строки сетки h3.

ex2.png

Вот варианты:

1. x = h3 * round((h1 - h2) / (2 * h3))
2. x = h3 * (h1 - h2) / 2
3. x = h3 * floor((h1 + h2) / 2)

Смотрите правильный ответ

На этот раз решение более сложное, но это все еще математика и всего одна строка. Как бы мы сделали это с помощью CSS? С автомакетом iOS? Макет Android?

К сожалению, нет никакого способа.

У кого есть ритм?

Эти головоломки были вдохновлены реальными вариантами использования графических интерфейсов. Последний
один является моделью Вертикальный ритм, концепция из
типография.

вертикальный-ритм.png

Почему это так сложно реализовать? В конце концов, мы просто располагаем пиксели на экране. Пиксели — это квадраты. Квадраты — это геометрические фигуры, которыми мы должны легко манипулировать. Но мы не можем.

Пиксель не пиксель

Проблема в том, что пиксель на экране представлен чем-то другим в системе. Это элемент DOM, представление, объект. Это тысячи разных вещей. Какой пиксель на самом деле скрыт от разработчика.

рендер-трубопровод.png

В случае браузера это приводит к разного рода неудобствам:

  • Мы ограничены вариантами использования, которые производители браузеров считают распространенными;
  • Мы не можем писать тесты для макета или рисовать этапы рендеринга;
  • Тестирование всего, что связано с пользовательским интерфейсом, требует отключение браузера. Это как покупать продукты на частном самолете.

Неправильный компромисс

В начале статьи я сказал, что основная проблема здесь заключается в следующем:

Плохой баланс контроля и усилий в наборе вариантов использования.

Давайте распакуем это заявление.

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

Усилия — это время, необходимое для реализации варианта использования с использованием API. Чем больше времени это занимает (для человека, который уже знает, как работает API), тем сложнее его использовать.

Под «контролем» я подразумеваю то, насколько детализированными могут быть команды, которые мы отправляем в API. В графическом API уровни управления могут варьироваться от изменения свойств компонента до изменения свойств формы и управления пикселем.

Объем нашего контроля часто определяется уровнем абстракции, с которым мы работаем. Чем ниже уровень абстракции, тем ближе мы к оборудованию, тем больше у нас контроля.

усилие-абстракция-graph.png

Может показаться, что эти два параметра, усилие и контроль, находятся в прямом противоречии. Чем больше у нас контроля, тем больше работы мы должны сделать. Это правда, но это не вся правда.

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

Теперь вспомним пример с сеткой в ​​начале статьи. Некоторые варианты использования просто невозможны с абстракциями высокого уровня. Пока поставщики API не сочтут такой вариант использования достаточно распространенным, мы не сможем его реализовать. В то же время это несложно сделать с помощью низкоуровневого API с высоким уровнем контроля, такого как OpenGL.

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

#ИспользуйтеПлатформу

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

браузер-api-1.png

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

браузер-api-2.png

Это привело нас к ситуации, в которой мы находимся сегодня. Есть этот плоский API для общих случаев с кучей «дыр», просверленных в нем, чтобы получить доступ к функциональным возможностям более низкого уровня, которые есть у браузеров (и были какое-то время).

Вот почему мы можем рисовать произвольные фигуры на холсте HTML, но мы не можем сделать эти фигуры надлежащей частью DOM, CSSOM или AOM (объектная модель специальных возможностей).

Мы можем использовать разные модели макетов, такие как flexbox или grid. Но нет никакого способа
мы можем провести модульное тестирование результатов этапа макета.

Мы можем до некоторой степени контролировать, что браузер хранит в своем кеше, но мы не можем сохранять, извлекать и манипулировать содержимым файла в нашем коде. В какой-то момент мы сможем сделать это благодаря File API. Но это будет отдельная конструкция, не связанная с Cache, Service Workers, Application Cache и другими API. Хотя все те иметь один и тот же фундамент ниже по стеку.

браузер-api-3.png

Трудный путь

Одним из способов разрешения этого конфликта является расстановка приоритетов либо в усилиях, либо в контроле. Есть еще один способ. Гораздо труднее.

Как разработчики API, мы можем решить представить его слоями. Чтобы предоставить доступ как к низкоуровневым, так и к высокоуровневым примитивам. Сложность заключается в том, чтобы сделать это таким образом, чтобы разработчик мог возиться с низкоуровневым API без того, чтобы результат был «исключен» из остальной системы.

Флаттер хороший пример такого подхода. Flutter — это кроссплатформенная платформа для разработки мобильных приложений, разработанная Google.

флаттер.png

Благодаря многоуровневой структуре API Flutter мы можем:

  • Сделать виджет, отвечающий за размещение своих дочерних элементов;
  • Модульное тестирование нашего пользовательского виджета макета без запуска эмулятора;
  • Создайте виджет с пользовательским методом рисования. Например, мы можем сделать кнопку с нестандартной формой, интересной тенью или цветовым эффектом;
  • Этот пользовательский виджет по-прежнему останется кнопкой с точки зрения принимаемых им жестов, доступности и других свойств;
  • Мы также можем выполнить модульное тестирование операции рисования. Опять же, просто запустив код на виртуальной машине Dart, эмулятор не требуется.

Вернемся к нашему примеру сетки из начала статьи. Вот код виджета, который центрирует свои дочерние элементы по вертикали, привязывая их к сетке:

Offset getPositionForChild(
  Size containerSize,
  Size childSize
) {
  final h1 = containerSize.height;
  final h2 = childSize.height;
  final h3 = rowHeight;
  final rowsCount = ((h1 - h2) / (2 * h3)).round();
  return Offset(0, rowsCount * h3);
}

Как видите, код во Flutter очень похож на то, что мы придумали с помощью Math: x = h3 * round((h1 - h2) / (2 * h3)). Это еще одно преимущество работы на правильном уровне абстракции: то, как мы выражаем свое намерение, прямолинейно.

Вот как выглядит получившееся приложение во Flutter:

флаттер-запись-обрезанный.mp4

Заключительные мысли

Эти принципы «усилия против конфликта», «многоуровневое против плоского» помогли мне решить
некоторые повторяющиеся проблемы в моей работе. Это полезный объектив для просмотра
через.

Иногда нам приходится «сверлить» наш плоский высокоуровневый API для извлечения
функциональность, которая скрыта под ним. Это знак того, что мы могли бы
нужно изменить наш подход. Помните, что есть гораздо больший набор использования
случаи
которые мы могли бы поддерживать с помощью API более низкого уровня.
Извлечение каждого варианта использования по одному приводит к раздуванию дизайна и более
Работа
от вас как дизайнера API.


Этот пост был изначально опубликовано в моем блоге.

Если вы хотите узнать больше о различных подходах к разработке графического интерфейса, см. этот эпизод подкаста Code.

Эта статья основана на моем выступлении по дизайну API. Ты можешь найти видео этого разговора здесь.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *