Files
cherrypick/doc/presentation_ru.md
Sergey Penkovsky 1b0615810d add presentation
2025-09-08 15:48:58 +03:00

8.5 KiB
Raw Blame History

marp
marp
true

CherryPick 3.x

Быстро. Безопасно. Просто.

Современный DI-framework для Dart и Flutter
Автор: Сергей Пеньковский


Что такое CherryPick?

  • Лёгкий и модульный framework для внедрения зависимостей (DI)
  • Фокус: производительность, безопасность и лаконичный код
  • Применяется во frontend, backend, CLI

Эволюция: что нового в 3.x?

  • Оптимизация скорости разрешения зависимостей
  • Интеграция с Talker для наглядного логирования DI-событий
  • Защита от циклических зависимостей на уровне ядра
  • Полностью декларативное описание DI через аннотации и генерацию кода
  • Автоматическая очистка ресурсов

Быстро

  • Мгновенное разрешение зависимостей

Мгновенное разрешение зависимостей

  • Операция resolve теперь выполняется за O(1)
  • Используется Map-индексация всех биндингов в каждом скоупе (в среднем ускорение в 10x+ на крупных графах)
  • Производительность не зависит от размера приложения

Безопасно

  • Циклические зависимости больше не страшны
  • Интеграция с Talker и расширенное логирование

Циклические зависимости больше не страшны

  • CherryPick 3.x автоматически выявляет циклы при разрешении зависимостей.
  • Возможна проверка как внутри отдельного scope, так и во всём DI-графе (глобально).

Как включить проверку циклов

  • Для защиты только внутри одного scope:
// 1. Для текущего scope (локальная проверка)
final scope = CherryPick.openRootScope();
scope.enableCycleDetection();
  • Для защиты всей иерархии скоупов:
// 2. Для всей иерархии скоупов (глобальная проверка)
CherryPick.enableGlobalCycleDetection();
CherryPick.enableGlobalCrossScopeCycleDetection();
final rootScope = CherryPick.openRootScope();

Пример обработки ошибки

При обнаружении цикла будет выброшено исключение с подробной трассировкой:

try {
  scope.resolve<A>();
} on CircularDependencyException catch(e) {
  print(e.dependencyChain);
}
=== Circular Dependency Detection Example ===

1. Attempt to create a scope with circular dependencies:
❌ Circular dependency detected: CircularDependencyException: Circular dependency detected for UserService
Dependency chain: UserService -> OrderService -> UserService

Интеграция с Talker и расширенное логирование

  • Всё, что происходит в DI: регистрация, создание, удаление, ошибки ― теперь логируется!
  • Достаточно подключить observer:
  final talker = Talker();
  final talkerLogger = TalkerCherryPickObserver(talker);
  CherryPick.setGlobalObserver(talkerLogger);
  • Логи сразу видны в консоли, UI
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ [info] | 9:41:33 89ms | [scope opened][CherryPick] scope_1757054493089_7072
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ [verbose] | 9:41:33 90ms | [diagnostic][CherryPick] Scope created: scope_1757054493089_7072 {type: Scope, name: scope_1757054493089_7072, description: scope created}
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────

Просто

  • Декларативный DI
  • Автоматическая очистка ресурсов

Декларативный DI: аннотации и генерация кода

  • Описывайте зависимости с помощью аннотаций
  • Автоматически генерируется модуль DI и mixin для автоподстановки зависимостей
@module()
abstract class AppModule extends Module {
  @provide()
  @singleton()
  Api api() => Api();
  @provide()
  Repo repo(Api api) => Repo(api);
}

Регистрация модуля

final scope = openRootScope()
  ..installModules([$AppModule()]);

Field injection: минимум кода — максимум удобства

@injectable()
class MyScreen  extends StatelessWidget with _$MyScreen {
  @inject()
  late final Repo repo;

  MyScreen() {
    _inject(this);
  }
}
  • После генерации mixin и вызова screen._inject() — зависимости готовы
  • Сильная типизация, никаких ручных вызовов resolve

Автоматическая очистка ресурсов

Автоматическая очистка ресурсов (контроллеры, потоки, сокеты, файлы и др.).

Если вы регистрируете объект, реализующий Disposable, через DI-контейнер, CherryPick вызовет его метод dispose() при закрытии скоупа.

class MyServiceWithSocket implements Disposable {
  @override
  Future<void> dispose() async {
    await socket.close();
    print('Socket закрыт!');
  }
}

scope.installModules([
  Module((bind) => bind<MyServiceWithSocket>().toProvide(() => MyServiceWithSocket()).singleton()),
]);

await CherryPick.closeRootScope(); // дождётся завершения async очистки

Почему это удобно?

Сравнение с ручным DI

Аннотации Ручной DI
Гибко
Кратко
Безопасно (легко ошибиться)

CherryPick 3.x: ваш DI-фреймворк

  • Быстрое разрешение зависимостей
  • Гарантия безопасности и тестируемости
  • Интеграция с логированием
  • Максимально простой и декларативный код

Спасибо за внимание


Вопросы?