Перейти к основному содержимому

Кодогенерация

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

Почему это важно. Написанный руками fromJson — то место, где живут тихие ошибки с данными: переименованное поле, поле, которое оказалось nullable, дата, разобранная не в том формате. Генератор не устаёт. Плата за это — лишний шаг сборки и целый класс сообщений об ошибках, которые ничего не объясняют, пока вы не столкнётесь с ними впервые.

Что понимать

  • Что генерируется, из чего и где лежит результат
  • Какой файл — источник истины (и это никогда не сгенерированный)
  • О чём на самом деле говорит упавшая сборка
  • Во что генерация обходится на каждой сборке и как удержать эту цену
  • Окупает ли генератор ту зависимость, которую приносит

Основные темы

Как это устроено

  • build_runner, билдеры и part-файлы
  • build, watch и --delete-conflicting-outputs
  • Сгенерированные файлы в системе контроля версий: аргументы обеих сторон и как всё-таки выбрать
  • Скорость сборки и почему она падает по мере роста проекта

Что генерируют

  • JSON-сериализацию: имена, значения по умолчанию, неизвестные поля
  • Неизменяемые классы данных: copyWith, равенство, union-типы
  • Внедрение зависимостей и service locator
  • Маршруты и таблицы навигации
  • Локализацию
  • Моки для тестов

Как с этим жить

  • Читать сгенерированный код, когда что-то сломалось
  • Отлаживаться сквозь сгенерированный слой
  • Держать генераторы согласованными при обновлении версий
  • Во что обходится генератор, который перестали поддерживать

Альтернативы

  • Макросы и куда движется язык
  • Написать руками, когда полей всего три
  • Фреймворк, который убирает саму необходимость, вместо того чтобы обкладывать её генераторами

Уровни

УровеньКак это выглядит
JuniorЗапускает build_runner, когда скажут, пользуется сгенерированными моделями, а сбои сборки воспринимает как магию.
MiddleНастраивает генераторы, при отладке читает сгенерированный код, разбирается с конфликтами и несовпадением версий.
SeniorРешает, что вообще стоит генерировать, держит время сборки в разумных пределах и взвешивает зависимость от генератора против того, чтобы один раз написать код руками.

Практика

Для начала

  • Прочитайте сгенерированный код Откройте .g.dart для модели, которой пользуетесь, и проследите, как разбирается одно поле.

  • Сломайте намеренно Переименуйте поле в ответе API и посмотрите, где всплывёт ошибка.

  • Возьмите отображение под контроль Сделайте имя поля в Dart отличным от ключа в JSON и обработайте случай, когда значения нет.

Глубже

  • Опишите union-тип Сгенерируйте sealed-объединение для ответа, который действительно бывает разной формы.

  • Ускорьте сборку Измерьте время работы build_runner и сократите его: ограничьте область билдеров, уберите лишнее.

  • Уберите генератор Найдите тот, что приносит меньше, чем стоит, и замените его пятнадцатью строками на Dart.

Проверьте себя

  • Где в вашем проекте лежит сгенерированный код и попадает ли он в репозиторий?
  • Что произойдёт, если API добавит поле, о котором вы не знаете?
  • Сколько занимает полная сборка и когда вы в последний раз это проверяли?
  • Если ошибка внутри сгенерированного класса — как вы её отлаживаете?
  • Потерю какого из ваших генераторов было бы больнее всего пережить и приемлемо ли это?
  • Что из того, что вы генерируете, руками вышло бы короче?

Материалы

  • build_runner — официальное руководство по запуску билдеров, включая режим watch и тот самый флаг для конфликтов, который рано или поздно нужен всем.
  • json_serializable — стандарт для сериализации. В README читать стоит прежде всего раздел про конфигурацию.
  • freezed — неизменяемые классы, union-типы и copyWith. Самый наглядный пример того, как генерация даёт настоящую выразительность модели, а не просто экономит нажатия клавиш.
  • package:build documentation — как билдеры устроены изнутри; понадобится в тот день, когда придётся написать или отладить свой.
  • Dart macros — куда движется язык; за этим стоит следить, прежде чем закладываться на большой объём генерации.