Кодогенерация
Сериализация, неизменяемые классы данных, связывание зависимостей и таблицы
маршрутов, которые пишет билдер, а не человек. В 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 — куда движется язык; за этим стоит следить, прежде чем закладываться на большой объём генерации.