Изучение

Начало работы

Конфигурация, разрешение и закрытие небольшого контейнера Ark DI.

v1.1.0Начальный уровеньark_di

Начало работы

На этой странице создаётся небольшой граф объектов с тремя разными временами жизни. Важно не только получить CatalogService, но и понимать, какой scope создаёт, удерживает и закрывает каждый объект.

1. Добавьте пакет

Терминал
shell
dart pub add ark_di
dart
import 'package:ark_di/ark_di.dart';

2. Не связывайте прикладные объекты с контейнером

Зависимости остаются обычными параметрами конструкторов:

dart
final class AppConfig {
  const AppConfig(this.apiBaseUrl);

  final Uri apiBaseUrl;
}

final class ApiClient {
  ApiClient(this.config);

  final AppConfig config;

  Future<void> close() async {}
}

final class CatalogService {
  CatalogService(this.client);

  final ApiClient client;
}

Эти классы не импортируют Ark DI и не обращаются к глобальному Service Locator. Способ сборки графа известен только корню композиции приложения.

3. Один раз сконфигурируйте граф

dart
final AppConfig config = AppConfig(Uri.parse('https://api.example.com'));

final DiContainer container = DiContainer.build((binder) {
  binder.bindInstance<AppConfig>(config);
  binder.bindLazySingleton<ApiClient>(
    (resolver) => ApiClient(resolver.get<AppConfig>()),
    dispose: (client) => client.close(),
  );
  binder.bindFactory<CatalogService>(
    (resolver) => CatalogService(resolver.get<ApiClient>()),
  );
});

Регистрации задают разное время жизни:

РегистрацияПоведение
bindInstanceСохраняет переданный AppConfig; по умолчанию контейнер владеет регистрацией.
bindLazySingletonПри первом разрешении создаёт один ApiClient, кэширует его и вызывает disposer при закрытии.
bindFactoryПри каждом разрешении создаёт новый CatalogService и не удерживает его.

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

4. Разрешите граф на границе композиции

dart
final CatalogService first = container.get<CatalogService>();
final CatalogService second = container.get<CatalogService>();

assert(!identical(first, second));
assert(identical(first.client, second.client));

Сервисы остаются временными объектами, но оба получают один принадлежащий контейнеру клиент. Затем приложение передаёт first нужной функции через обычный конструктор.

5. Закройте владельца

dart
await container.close();

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

Перед использованием дочерних scope и асинхронных регистраций прочитайте руководство по корню композиции.