mirror of
https://github.com/pese-git/cherrypick.git
synced 2026-01-23 13:03:11 +00:00
8.5 KiB
8.5 KiB
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-фреймворк
- Быстрое разрешение зависимостей
- Гарантия безопасности и тестируемости
- Интеграция с логированием
- Максимально простой и декларативный код
Спасибо за внимание
Вопросы?
- Try CherryPick - https://pub.dev/packages/cherrypick
- Contributing — https://github.com/pese-git/cherrypick
- Документация и примеры — https://cherrypick-di.netlify.app
- Готов помочь — пишите, пробуйте, внедряйте!