Внедрение зависимостей во Flutter — последствия различных подходов

Во Flutter я чувствую себя немного потерянным из-за того, как я создаю свои параметры для классов, и знаю, как лучше всего создавать эти параметры. Обычно параметры — это просто классы для внедрения в другой класс для выполнения задач. Кажется, что на самом деле не имеет значения, как эти параметры создаются с точки зрения функциональности, поскольку код работает со всеми методами создания. Я вижу онлайн-людей, говорящих о локаторах сервисов, синглтонах, внедрении зависимостей. Заявления Riverpod на веб-сайте Поставщики — это полная замена таких шаблонов, как синглтоны, локаторы сервисов, внедрение зависимостей или наследуемые виджеты. Так что я думаю, что мне не нужен сервисный локатор, так как я использую Riverpod. Однако я не могу найти в Интернете ничего о том, как я могу внедрить услугу у провайдеров Riverpod. Я вижу, что вы можете прочитать поставщика без контекста с помощью ProviderContainer().read, но это для использования в качестве внедрения службы? Является ли это синглтоном, так что в значительной степени он является сервисным локатором? В примере с Riverpod говорится, что вам не нужен ProviderContainer().read для Flutter, что звучит так, как будто это не замена чему-либо вроде локатора сервисов:

  // Where the state of our providers will be stored.
  // Avoid making this a global variable, for testability purposes.
  // If you are using Flutter, you do not need this.
  final container = ProviderContainer();

Вот пример кода, поле в классе, которое является ViewModel, которое принимает некоторые варианты использования в качестве параметров. Примерами использования здесь являются классы действий уровня домена, которые вызывают репозитории для выполнения внешних действий, таких как запросы API или манипуляции с локальным хранилищем.

  final CreateUserViewModel createUserViewModel =
      CreateUserViewModel(SavePasswordLocallyUseCase(), CreateUserUseCase());
...

Так что я буквально создал их в строке, как SavePasswordLocallyUseCase(), чтобы вводить их. Это определенно самый простой подход с наименьшим количеством кода. Я предполагаю, что это может быть менее эффективно, поскольку каждый раз создается новый, хотя я не вижу, чтобы это обычно имело видимую разницу. Будут ли эти параметры, созданные таким образом, очищаться сборщиком мусора? Каковы последствия этого?

Если бы мне пришлось внедрить тип AuthenticationService, должен ли я использовать локатор сервисов или создавать их встроенными, как AuthenticationService(), или использовать ProviderContainer.read() от Riverpod?


person BeniaminoBaggins    schedule 11.12.2020    source источник


Ответы (1)


Оглядевшись, я вижу, что внедрение зависимостей во Flutter в основном выполняется путем создания нового экземпляра параметра для внедрения в конструктор, как в моем вопросе:

final CreateUserViewModel createUserViewModel =
  CreateUserViewModel(SavePasswordLocallyUseCase(), CreateUserUseCase());

И что это очень быстро становится очень грязным, если внедренным классам также нужны свои собственные параметры. Пакеты внедрения зависимостей в основном нацелены на решение этой проблемы путем однократной настройки классов с их необходимыми параметрами, а затем вы можете просто запросить экземпляр из пакета внедрения зависимостей без необходимости снова создавать параметры его конструктора. Я считаю, что версия Riverpod действительно должна использовать ProviderContainer.read(), если не на уровне пользовательского интерфейса. Допустим, я хочу создать экземпляр класса, который принимает репозиторий в своем конструкторе:

class SavePasswordLocallyUseCase implements ISavePasswordLocallyUseCase {
  const SavePasswordLocallyUseCase(this._userRepository);
  final IRegistrationRepository _userRepository;
  String invoke(String password, String confirmPassword) {
    return _userRepository.putPasswords(password, confirmPassword);
  }

И самому внедренному репозиторию нужны 2 параметра конструктора:

class RegistrationRepository implements IRegistrationRepository {
  const RegistrationRepository(
      this._authenticationRemoteDataSource, this._registrationLocalDataSource);
    }
  final AuthenticationRemoteDataSource _authenticationRemoteDataSource;

  final IRegistrationLocalDataSource _registrationLocalDataSource;

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

new RegistrationRepository(AuthenticationRemoteDataSource(X()), RegistrationLocalDataSource(Y()))

Создав стандартный стандарт Provider в Riverpod, который устанавливает параметры один раз:

final registrationRepositoryProvider =
    Provider.autoDispose<RegistrationRepository>((ref) {
  ref.onDispose(() {
    print('disposing');
  });

  return RegistrationRepository(
      AuthenticationRemoteDataSource(X()), RegistrationLocalDataSource(Y()));
});

Затем я могу разрешить классам доступ к RegistrationRepository следующим образом:

ref.container.read(registrationRepositoryProvider);

Или без ProviderReference:

ProviderContainer().read(registrationRepositoryProvider);

Я до сих пор не уверен насчет ленивой загрузки и синглтонов в Riverpod и возможны ли эти варианты. Возможно, было бы удобнее использовать Injectable для внедрения зависимостей, которых нет в представлении, что Я рассматриваю.

person BeniaminoBaggins    schedule 11.12.2020