Внедрение зависимостей во Flutter во время компиляции

Эй, ребята! Я вернулся с еще одной совершенно новой статьей о Flutter. На этот раз я расскажу об очень интересном и важном шаблоне проектирования в мире разработки программного обеспечения, а именно о «внедрении зависимостей». Это поможет вам создавать слабосвязанные классы, что в конечном итоге приведет к поддерживаемой и тестируемой кодовой базе. Так что возьмите чашку кофе, найдите тихое место и узнайте, как я использую DI в проекте Flutter.

Содержание

  1. Что такое внедрение зависимостей?
  2. Добавление inject.dart package как подмодуль к проекту
  3. Понимание аннотаций(@provide, @module и т. д.)
  4. Написание модулей и класса Injector
  5. Разрешение зависимостей во время компиляции 😍
  6. Соберите и запустите приложение 🏃

В этой статье я покажу вам, как провести рефакторинг проекта Flutter, в котором не используется какая-либо структура DI. Таким образом, чтобы в полной мере воспользоваться содержанием, я ожидаю, что читатель построил как минимум 2 или 3 приложения использовать Flutter и иметь некоторое базовое представление о шаблоне BLoC. Если вы новичок в шаблоне BLoC, то эти два ( ЧАСТЬ 1 а также Часть 2 ) статьи — лучшее место для начала, так как я буду рефакторить тот же проект, чтобы инжекторный блок а также хранилище объекты в дерево виджетов.

Что такое внедрение зависимостей?

Позвольте мне объяснить вам, взяв пример из реальной жизни. Вы знаете, как производятся мобильные телефоны? Позвольте мне объяснить в основных терминах. Для сборки мобильного телефона вам в основном нужны следующие компоненты:

  1. Печатная плата, содержащая мозг телефона.
  2. Антенна.
  3. Жидкокристаллический дисплей (LCD)
  4. Микрофон.
  5. Спикер.
  6. Батарея.

Теперь все эти компоненты не производятся на одном заводе. Эти компоненты производятся в разных странах (при условии). Затем все эти отдельные компоненты импортируются и собираются вместе на одном заводе. Это » Внедрение зависимости ». Сборочное производство было не зная процесса сборки различных компонентов. Единственной задачей фабрики была сборка этих компонентов для создания телефона. Таким образом, фабрике не нужно беспокоиться о процессе сборки этих компонентов, и она может сосредоточиться только на своей работе, т.е. «собирать необходимые компоненты для сборки телефона». Я думаю, что этого небольшого примера достаточно для начала, но для любопытных, которые хотят больше узнать о DI. Проверьте это удивительное статья .

Настраивать

Давайте импортируем Мои фильмы проект с GitHub и запустите его один раз, чтобы проверить, все ли работает нормально.

Итак, если вы посмотрите на проект. У меня есть структура пакета ниже. Я ожидаю, что даже у вас есть то же самое. Просто убедитесь, что мы на одной странице:

Проблема

Позвольте мне показать вам один пример того, в чем проблема с текущей структурой кода. Если вы откроете movie_api_provider.dart файл и увидите вторую строку, вы обнаружите, что я создаю новый Client() предмет внутри MovieApiProvider учебный класс. Но согласно объяснению, которое я дал вам в предыдущем разделе о DI. Этого не должно быть. MovieApiProvider не должны нести ответственность за создание Client объект, но вместо этого он должен получить Client объект, предоставленный кем-то.

Это только один пример. Если вы проверите другие классы, такие как repository.dart а также movies_bloc.dart вы найдете тот же случай. Эти классы не должны нести ответственность за создание объектов классов, от которых они зависят. Вместо этого они(repository.dart или же movies_bloc.dart) должны быть обеспечены требуемыми объектами из какого-либо другого класса или фабрики.

Еще одна замечательная особенность этого шаблона проектирования (DI) заключается в том, что нам даже не нужно BlocProviders (InheritedWidget) больше, чтобы предоставить нам блок или любой другой тип объекта. InheritedWidget не будет разрешать зависимости во время сборки, но выдаст исключение во время выполнения, если не удалось правильно подключить зависимости.

Итак, как мы можем снять с этих классов ответственность за создание объектов? Как мы можем создать фабрику, которая будет нести ответственность за создание и предоставление зависимых объектов этим классам? Давай выясним. 😃

inject.dart нам в помощь

мы будем использовать inject.dart для решения проблем, о которых я говорил выше. Это с открытым исходным кодом время компиляции пакет внедрения зависимостей для Dart и Flutter из внутреннего репозитория внутри Google. Этот пакет поможет нам построить фабрику, где мы сможем создавать все зависимые объекты отдельно и внедрять их туда, где они требуются. Самое приятное в этом пакете то, что он разрешит все зависимости во время сборки. Не будет никаких исключений во время выполнения, связанных с подключением зависимостей к провайдерам. Другими словами, перед запуском приложения убедитесь, что все объекты созданы или все зависимости разрешены. Удивительно, не так ли?

Этот фреймворк не «утекает», не имеет побочных эффектов и не требует зависимостей для его использования. Этот фреймворк использует только ядро dart:* библиотеки, поэтому он не зависит от платформы (Flutter или AngularDart).

Добавление inject.dart Добавление этого пакета будет немного отличаться от того, как мы обычно добавляем плагины в наш проект. Создайте папку и назовите ее injection (можно назвать что угодно) внутри MyMovies каталог. Откройте терминал, перейдите в папку, которую вы только что создали, и выполните следующую команду:

git submodule add

Теперь вы увидите следующую структуру пакета:

Не обращайте внимания на эти ошибки. Это не будет препятствием для нашего продвижения.

Добавьте следующие зависимости в свой pubspec.yaml:

flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2
  rxdart: ^0.18.0
  http: ^0.12.0+1
  inject:
    path: ./injection/inject.dart/package/inject

dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^1.0.0
  inject_generator:
    path: ./injection/inject.dart/package/inject_generator

build_runner package предоставляет конкретный способ создания файлов с использованием кода Dart. В конце концов, когда мы будем собирать проект, вы будете использовать build_runner.

Создайте новый пакет под src и назови это di . Теперь добавьте два файла dart в пакет di и назовите их как bloc_injector.dart а также bloc_module.dart .

Написание модулей и класса Injector

Приступаем к работе над bloc_module.dart сначала файл. Скопируйте и вставьте следующий код в файл:

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

Понимание аннотаций

@module : Эта аннотация сделает BlocModule класс, доступный для внесения вклада в граф зависимостей, который inject.dart будет генерировать. Он будет содержать все зависимости, требуемые другими классами.

@provide : все методы, аннотированные этим, доступны для внедрения зависимостей. Проверка приведенного выше кода прояснит ситуацию.

@singleton : Как следует из названия, он создаст и предоставит один экземпляр объекта.

Теперь откройте bloc_injector.dart и добавьте следующий код в файл:

Вы должны видеть некоторые ошибки. Но не беспокойтесь, bloc_injector.inject.dart файл будет создан во время сборки, и все будет решено. Как только файл будет сгенерирован, мы изучим create() также метод. Еще одна вещь, которую следует отметить здесь, это App get app; это корневой виджет нашего проекта, и мы объявляем его здесь, чтобы указать место назначения, куда будут внедрены зависимости.

@Injector : Помните, ранее мы говорили о фабрике, которая будет собирать зависимые объекты. Это то, что будет делать эта аннотация. Он все склеит и сделает все объекты доступными для инъекций. Это принимает все зависимые модули в качестве аргумента, потому что именно так инжектор узнает, какие все объекты должны быть разрешены и предоставлены.

Открытым main.dart файл и вставьте в него следующий код:

Я объясню вам, что это container объект после того, как мы сгенерируем bloc_injector.inject.dart файл.

Открытым app.dart файл и вставьте в него следующий код:

Вы увидите некоторые ошибки, но мы скоро их исправим. В приведенном выше фрагменте кода оба MoviesBloc а также MoviesDetailBloc объекты предоставляются посредством внедрения конструктора, а затем передаются соответствующим классам, т.е. MovieList а также MovieDetail экран. Здесь нет создания объекта. Это чертовски удивительно! 😲 👏

Создайте новый файл под blocs пакет и назовите его как bloc_base.dart . Вставьте в него приведенный ниже код:

Открытым movie_detail_bloc.dart файл и вставьте в него следующий код:

Как вы можете видеть в приведенном выше коде, я не создаю Repository объект внутри MovieDetailBloc учебный класс. Вместо этого я ввел объект в качестве аргумента конструктора. 😲 😲

Открытым movies_bloc.dart файл и вставьте в него следующий код:

То же самое! я не создаю Repository предмет внутри MovieBloc учебный класс. Вместо этого он будет предоставлен откуда-то извне. Ух ты!! 😲

Открытым movie_api_provider.dart файл и вставьте ниже код:

Как видите, я не создаю Client предмет внутри MovieApiProvider учебный класс. @provide используется, чтобы помочь платформе DI выяснить, где требуются все зависимости, и соответствующим образом сгенерировать код.

Открытым repository.dart файл и вставьте в него следующий код:

Лучшая часть, movieApiProvider объект будет создан только один раз, потому что мы аннотировали MovieApiProvider как singleton при создании в BlocModule.

Открытым movie_detail.dart файл и вставьте ниже код:

Здесь MovieDetailBloc вводится как параметр конструктора. И есть небольшое изменение в том, как bloc объект используется в initState() . Не стесняйтесь исследовать init() метод. я не использую MovieDetailBlocProvider (InhertiedWidget) class больше, чтобы получить доступ к bloc объект. мне даже не нужен context больше. 😉

Открытым movie_list.dart файл и вставьте в него следующий код:

Здесь такие же удивительные вещи. MoviesBloc объект вводится сюда через конструктор.

Открытым item_model.dart файл и замените этот код:

Я только что сделал небольшое изменение. Я сделал класс Result общедоступным, чтобы мы могли передавать аргументы типа Result по названным маршрутам.

Мы закончили с изменением структуры кода. Теперь сосредоточимся на том, как скомпилировать проект. Это действительно важный шаг. Создайте новый файл в каталоге проекта и назовите его как inject_generator_build.yaml . Вот где это должно быть размещено:

Скопируйте и вставьте в него приведенный ниже код:

Мы создали этот файл, потому что по умолчанию все сгенерированные файлы будут помещаться в папку кеша, и Flutter не сможет получить доступ к файлам оттуда (но над решением этой проблемы ведется работа). Так что будем менять build_to: cache к build_to: source .

Разрешение зависимостей во время компиляции 😍

Пришло время сгенерировать код 🔥. Откройте терминал и перейдите в папку проекта. Запустите команду ниже:

flutter packages pub run build_runner build

Соберите и запустите приложение 🏃

Если build_runner удалось разрешить все зависимости, вы увидите следующий вывод:

Поздравляю! Если вы добрались до сюда. Вы успешно создали граф зависимостей. Ты гений. 👏 👏 👏 (Если у вас возникнут какие-либо проблемы, дайте мне знать о вашей проблеме, оставив комментарий или связавшись со мной через LinkedIn или Twitter).

Очистка

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

Не паникуйте! Это нормально. Вы можете удалить все *.inject.dart а также *.summary файлы кроме bloc_injector.inject.dart . Вы даже можете удалить movie_detail_bloc_provider.dart файл. Чтобы автоматизировать эту задачу. Перейдите в lib и другие подкаталоги src и запустите команду ниже (это удалит только пустые файлы):

find . -size 0 -delete

Изучение сгенерированного графа

Вот Это Да! Это выглядит так красиво ❤️. Как вы можете видеть в сгенерированном коде, оба MoviesBloc а также MovieDetailBloc объекты передаются как параметры конструктора в App Виджет. Если вы внимательно просмотрите этот файл, вы поймете цель BlocModule а также bloc_injector.dart файл.

Отправка вашего проекта на git

Прежде чем отправлять этот вновь созданный проект в свою учетную запись git, добавьте следующую строку в свой .gitignore файл:

*.inject.summary

Это действительно большое и мощное изменение, которое мы внесли в наш проект. Теперь вы можете легко внедрить объекты любого типа в дерево виджетов извне. Таким образом, вы сохраните свою кодовую базу в сопровождении и тестировании. В моей следующей статье я покажу вам, как протестировать ваш код с помощью внедрения зависимостей.

Я создал отдельный ответвляться для этой реализации. Это поможет вам сравнить обе реализации (без DI и с DI) и в конечном итоге выбрать то, что соответствует вашим потребностям.

Надеюсь, вам понравилась статья. Покажите свою любовь к моей работе, поставив лайк этой статье. Не стесняйтесь связаться со мной в Твиттер или же LinkedIn за любую помощь или запрос.

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

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

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