diff --git a/benchmark_di/lib/benchmarks/universal_chain_async_benchmark.dart b/benchmark_di/lib/benchmarks/universal_chain_async_benchmark.dart index 55b225e..0e7412f 100644 --- a/benchmark_di/lib/benchmarks/universal_chain_async_benchmark.dart +++ b/benchmark_di/lib/benchmarks/universal_chain_async_benchmark.dart @@ -2,10 +2,9 @@ import 'package:benchmark_harness/benchmark_harness.dart'; import 'package:benchmark_di/di_adapters/di_adapter.dart'; import 'package:benchmark_di/scenarios/universal_chain_module.dart'; import 'package:benchmark_di/scenarios/universal_service.dart'; -import 'package:benchmark_di/scenarios/di_universal_registration.dart'; -class UniversalChainAsyncBenchmark extends AsyncBenchmarkBase { - final DIAdapter di; +class UniversalChainAsyncBenchmark extends AsyncBenchmarkBase { + final DIAdapter di; final int chainCount; final int nestingDepth; final UniversalBindingMode mode; @@ -19,8 +18,7 @@ class UniversalChainAsyncBenchmark extends AsyncBenchmarkBase { @override Future setup() async { - di.setupDependencies(getUniversalRegistration( - di, + di.setupDependencies(di.universalRegistration( chainCount: chainCount, nestingDepth: nestingDepth, bindingMode: mode, diff --git a/benchmark_di/lib/benchmarks/universal_chain_benchmark.dart b/benchmark_di/lib/benchmarks/universal_chain_benchmark.dart index d814709..b777bf0 100644 --- a/benchmark_di/lib/benchmarks/universal_chain_benchmark.dart +++ b/benchmark_di/lib/benchmarks/universal_chain_benchmark.dart @@ -2,15 +2,14 @@ import 'package:benchmark_harness/benchmark_harness.dart'; import 'package:benchmark_di/di_adapters/di_adapter.dart'; import 'package:benchmark_di/scenarios/universal_chain_module.dart'; import 'package:benchmark_di/scenarios/universal_service.dart'; -import 'package:benchmark_di/scenarios/di_universal_registration.dart'; -class UniversalChainBenchmark extends BenchmarkBase { - final DIAdapter _di; +class UniversalChainBenchmark extends BenchmarkBase { + final DIAdapter _di; final int chainCount; final int nestingDepth; final UniversalBindingMode mode; final UniversalScenario scenario; - DIAdapter? _childDi; + DIAdapter? _childDi; UniversalChainBenchmark( this._di, { @@ -24,25 +23,22 @@ class UniversalChainBenchmark extends BenchmarkBase { void setup() { switch (scenario) { case UniversalScenario.override: - _di.setupDependencies(getUniversalRegistration( - _di, + _di.setupDependencies(_di.universalRegistration( chainCount: chainCount, nestingDepth: nestingDepth, bindingMode: UniversalBindingMode.singletonStrategy, scenario: UniversalScenario.chain, )); _childDi = _di.openSubScope('child'); - _childDi!.setupDependencies(getUniversalRegistration( - _childDi!, + _childDi!.setupDependencies(_childDi!.universalRegistration( chainCount: chainCount, nestingDepth: nestingDepth, bindingMode: UniversalBindingMode.singletonStrategy, - scenario: UniversalScenario.chain, // критично: цепочку, а не просто alias! + scenario: UniversalScenario.chain, )); break; default: - _di.setupDependencies(getUniversalRegistration( - _di, + _di.setupDependencies(_di.universalRegistration( chainCount: chainCount, nestingDepth: nestingDepth, bindingMode: mode, diff --git a/benchmark_di/lib/cli/benchmark_cli.dart b/benchmark_di/lib/cli/benchmark_cli.dart index fd26052..4c1ae35 100644 --- a/benchmark_di/lib/cli/benchmark_cli.dart +++ b/benchmark_di/lib/cli/benchmark_cli.dart @@ -1,6 +1,9 @@ import 'dart:math'; import 'package:benchmark_di/cli/report/markdown_report.dart'; +import 'package:cherrypick/cherrypick.dart'; +import 'package:get_it/get_it.dart'; +import 'package:riverpod/riverpod.dart' as rp; import '../scenarios/universal_chain_module.dart'; import 'report/pretty_report.dart'; @@ -33,7 +36,7 @@ class BenchmarkCliRunner { if (config.di == 'getit') { final di = GetItAdapter(); if (scenario == UniversalScenario.asyncChain) { - final benchAsync = UniversalChainAsyncBenchmark(di, + final benchAsync = UniversalChainAsyncBenchmark(di, chainCount: c, nestingDepth: d, mode: mode, ); benchResult = await BenchmarkRunner.runAsync( @@ -42,7 +45,7 @@ class BenchmarkCliRunner { repeats: config.repeats, ); } else { - final benchSync = UniversalChainBenchmark(di, + final benchSync = UniversalChainBenchmark(di, chainCount: c, nestingDepth: d, mode: mode, scenario: scenario, ); benchResult = await BenchmarkRunner.runSync( @@ -54,7 +57,7 @@ class BenchmarkCliRunner { } else if (config.di == 'riverpod') { final di = RiverpodAdapter(); if (scenario == UniversalScenario.asyncChain) { - final benchAsync = UniversalChainAsyncBenchmark(di, + final benchAsync = UniversalChainAsyncBenchmark>>(di, chainCount: c, nestingDepth: d, mode: mode, ); benchResult = await BenchmarkRunner.runAsync( @@ -63,7 +66,7 @@ class BenchmarkCliRunner { repeats: config.repeats, ); } else { - final benchSync = UniversalChainBenchmark(di, + final benchSync = UniversalChainBenchmark>>(di, chainCount: c, nestingDepth: d, mode: mode, scenario: scenario, ); benchResult = await BenchmarkRunner.runSync( @@ -75,7 +78,7 @@ class BenchmarkCliRunner { } else { final di = CherrypickDIAdapter(); if (scenario == UniversalScenario.asyncChain) { - final benchAsync = UniversalChainAsyncBenchmark(di, + final benchAsync = UniversalChainAsyncBenchmark(di, chainCount: c, nestingDepth: d, mode: mode, ); benchResult = await BenchmarkRunner.runAsync( @@ -84,7 +87,7 @@ class BenchmarkCliRunner { repeats: config.repeats, ); } else { - final benchSync = UniversalChainBenchmark(di, + final benchSync = UniversalChainBenchmark(di, chainCount: c, nestingDepth: d, mode: mode, scenario: scenario, ); benchResult = await BenchmarkRunner.runSync( diff --git a/benchmark_di/lib/di_adapters/cherrypick_adapter.dart b/benchmark_di/lib/di_adapters/cherrypick_adapter.dart index d5cf865..756e1ed 100644 --- a/benchmark_di/lib/di_adapters/cherrypick_adapter.dart +++ b/benchmark_di/lib/di_adapters/cherrypick_adapter.dart @@ -1,7 +1,7 @@ import 'package:cherrypick/cherrypick.dart'; import 'di_adapter.dart'; +import '../scenarios/universal_chain_module.dart'; -/// Универсальный DIAdapter для CherryPick с поддержкой subScope без дублирования логики. class CherrypickDIAdapter extends DIAdapter { Scope? _scope; final bool _isSubScope; @@ -16,6 +16,28 @@ class CherrypickDIAdapter extends DIAdapter { registration(_scope!); } + @override + Registration universalRegistration({ + required S scenario, + required int chainCount, + required int nestingDepth, + required UniversalBindingMode bindingMode, + }) { + if (scenario is UniversalScenario) { + return (scope) { + scope.installModules([ + UniversalChainModule( + chainCount: chainCount, + nestingDepth: nestingDepth, + bindingMode: bindingMode, + scenario: scenario, + ), + ]); + }; + } + throw UnsupportedError('Scenario $scenario not supported by CherrypickDIAdapter'); + } + @override T resolve({String? named}) => named == null ? _scope!.resolve() : _scope!.resolve(named: named); diff --git a/benchmark_di/lib/di_adapters/di_adapter.dart b/benchmark_di/lib/di_adapters/di_adapter.dart index 3543cce..1ccfadb 100644 --- a/benchmark_di/lib/di_adapters/di_adapter.dart +++ b/benchmark_di/lib/di_adapters/di_adapter.dart @@ -1,9 +1,21 @@ +import 'package:benchmark_di/scenarios/universal_chain_module.dart'; + /// Универсальная абстракция для DI-адаптера с унифицированной функцией регистрации. /// Теперь для каждого адаптера задаём строгий generic тип контейнера. +typedef Registration = void Function(TContainer); + abstract class DIAdapter { /// Устанавливает зависимости с помощью строго типизированного контейнера. void setupDependencies(void Function(TContainer container) registration); + /// Возвращает типобезопасную функцию регистрации зависимостей под конкретный сценарий. + Registration universalRegistration({ + required S scenario, + required int chainCount, + required int nestingDepth, + required UniversalBindingMode bindingMode, + }); + /// Резолвит (возвращает) экземпляр типа [T] (по имени, если требуется). T resolve({String? named}); diff --git a/benchmark_di/lib/di_adapters/get_it_adapter.dart b/benchmark_di/lib/di_adapters/get_it_adapter.dart index 68079e6..051cdc5 100644 --- a/benchmark_di/lib/di_adapters/get_it_adapter.dart +++ b/benchmark_di/lib/di_adapters/get_it_adapter.dart @@ -1,3 +1,5 @@ +import 'package:benchmark_di/scenarios/universal_chain_module.dart'; +import 'package:benchmark_di/scenarios/universal_service.dart'; import 'package:get_it/get_it.dart'; import 'di_adapter.dart'; @@ -58,4 +60,96 @@ class GetItAdapter extends DIAdapter { Future waitForAsyncReady() async { await _getIt.allReady(); } + + @override + Registration universalRegistration({ + required S scenario, + required int chainCount, + required int nestingDepth, + required UniversalBindingMode bindingMode, + }) { + if (scenario is UniversalScenario) { + return (getIt) { + switch (scenario) { + case UniversalScenario.asyncChain: + for (int chain = 1; chain <= chainCount; chain++) { + for (int level = 1; level <= nestingDepth; level++) { + final prevDepName = '${chain}_${level - 1}'; + final depName = '${chain}_$level'; + getIt.registerSingletonAsync( + () async { + final prev = level > 1 + ? await getIt.getAsync(instanceName: prevDepName) + : null; + return UniversalServiceImpl(value: depName, dependency: prev); + }, + instanceName: depName, + ); + } + } + break; + case UniversalScenario.register: + getIt.registerSingleton(UniversalServiceImpl(value: 'reg', dependency: null)); + break; + case UniversalScenario.named: + getIt.registerFactory(() => UniversalServiceImpl(value: 'impl1'), instanceName: 'impl1'); + getIt.registerFactory(() => UniversalServiceImpl(value: 'impl2'), instanceName: 'impl2'); + break; + case UniversalScenario.chain: + for (int chain = 1; chain <= chainCount; chain++) { + for (int level = 1; level <= nestingDepth; level++) { + final prevDepName = '${chain}_${level - 1}'; + final depName = '${chain}_$level'; + switch (bindingMode) { + case UniversalBindingMode.singletonStrategy: + getIt.registerSingleton( + UniversalServiceImpl( + value: depName, + dependency: level > 1 + ? getIt(instanceName: prevDepName) + : null, + ), + instanceName: depName, + ); + break; + case UniversalBindingMode.factoryStrategy: + getIt.registerFactory( + () => UniversalServiceImpl( + value: depName, + dependency: level > 1 + ? getIt(instanceName: prevDepName) + : null, + ), + instanceName: depName, + ); + break; + case UniversalBindingMode.asyncStrategy: + getIt.registerSingletonAsync( + () async => UniversalServiceImpl( + value: depName, + dependency: level > 1 + ? await getIt.getAsync(instanceName: prevDepName) + : null, + ), + instanceName: depName, + ); + break; + } + } + } + break; + case UniversalScenario.override: + // handled at benchmark level + break; + } + if (scenario == UniversalScenario.chain || scenario == UniversalScenario.override) { + final depName = '${chainCount}_$nestingDepth'; + getIt.registerSingleton( + getIt(instanceName: depName), + ); + } + }; + } + throw UnsupportedError('Scenario $scenario not supported by GetItAdapter'); + } } diff --git a/benchmark_di/lib/di_adapters/riverpod_adapter.dart b/benchmark_di/lib/di_adapters/riverpod_adapter.dart index 15dc4bd..2788828 100644 --- a/benchmark_di/lib/di_adapters/riverpod_adapter.dart +++ b/benchmark_di/lib/di_adapters/riverpod_adapter.dart @@ -1,28 +1,30 @@ -import 'package:riverpod/riverpod.dart'; +import 'package:benchmark_di/scenarios/universal_chain_module.dart'; +import 'package:benchmark_di/scenarios/universal_service.dart'; +import 'package:riverpod/riverpod.dart' as rp; import 'di_adapter.dart'; /// Унифицированный DIAdapter для Riverpod с поддержкой scopes и строгой типизацией. -class RiverpodAdapter extends DIAdapter>> { - ProviderContainer? _container; - final Map> _namedProviders; - final ProviderContainer? _parent; +class RiverpodAdapter extends DIAdapter>> { + rp.ProviderContainer? _container; + final Map> _namedProviders; + final rp.ProviderContainer? _parent; final bool _isSubScope; RiverpodAdapter({ - ProviderContainer? container, - Map>? providers, - ProviderContainer? parent, + rp.ProviderContainer? container, + Map>? providers, + rp.ProviderContainer? parent, bool isSubScope = false, }) : _container = container, - _namedProviders = providers ?? >{}, + _namedProviders = providers ?? >{}, _parent = parent, _isSubScope = isSubScope; @override - void setupDependencies(void Function(Map> container) registration) { + void setupDependencies(void Function(Map> container) registration) { _container ??= _parent == null - ? ProviderContainer() - : ProviderContainer(parent: _parent); + ? rp.ProviderContainer() + : rp.ProviderContainer(parent: _parent); registration(_namedProviders); } @@ -59,7 +61,7 @@ class RiverpodAdapter extends DIAdapter>> { @override RiverpodAdapter openSubScope(String name) { - final newContainer = ProviderContainer(parent: _container); + final newContainer = rp.ProviderContainer(parent: _container); return RiverpodAdapter( container: newContainer, providers: Map.of(_namedProviders), @@ -73,4 +75,64 @@ class RiverpodAdapter extends DIAdapter>> { // Riverpod синхронный по умолчанию. return; } + + @override + Registration>> universalRegistration({ + required S scenario, + required int chainCount, + required int nestingDepth, + required UniversalBindingMode bindingMode, + }) { + if (scenario is UniversalScenario) { + return (providers) { + switch (scenario) { + case UniversalScenario.register: + providers['UniversalService'] = rp.Provider((ref) => UniversalServiceImpl(value: 'reg', dependency: null)); + break; + case UniversalScenario.named: + providers['impl1'] = rp.Provider((ref) => UniversalServiceImpl(value: 'impl1')); + providers['impl2'] = rp.Provider((ref) => UniversalServiceImpl(value: 'impl2')); + break; + case UniversalScenario.chain: + for (int chain = 1; chain <= chainCount; chain++) { + for (int level = 1; level <= nestingDepth; level++) { + final prevDepName = '${chain}_${level - 1}'; + final depName = '${chain}_$level'; + providers[depName] = rp.Provider((ref) => UniversalServiceImpl( + value: depName, + dependency: level > 1 ? ref.watch(providers[prevDepName] as rp.ProviderBase) : null, + )); + } + } + final depName = '${chainCount}_$nestingDepth'; + providers['UniversalService'] = rp.Provider((ref) => ref.watch(providers[depName] as rp.ProviderBase)); + break; + case UniversalScenario.override: + // handled at benchmark level + break; + case UniversalScenario.asyncChain: + for (int chain = 1; chain <= chainCount; chain++) { + for (int level = 1; level <= nestingDepth; level++) { + final prevDepName = '${chain}_${level - 1}'; + final depName = '${chain}_$level'; + providers[depName] = rp.FutureProvider((ref) async { + return UniversalServiceImpl( + value: depName, + dependency: level > 1 + ? await ref.watch((providers[prevDepName] as rp.FutureProvider).future) as UniversalService? + : null, + ); + }); + } + } + final depName = '${chainCount}_$nestingDepth'; + providers['UniversalService'] = rp.FutureProvider((ref) async { + return await ref.watch((providers[depName] as rp.FutureProvider).future); + }); + break; + } + }; + } + throw UnsupportedError('Scenario $scenario not supported by RiverpodAdapter'); + } } diff --git a/benchmark_di/lib/scenarios/di_universal_registration.dart b/benchmark_di/lib/scenarios/di_universal_registration.dart deleted file mode 100644 index 22d7666..0000000 --- a/benchmark_di/lib/scenarios/di_universal_registration.dart +++ /dev/null @@ -1,168 +0,0 @@ -import 'package:benchmark_di/scenarios/universal_service.dart'; - -import '../di_adapters/di_adapter.dart'; -import '../di_adapters/cherrypick_adapter.dart'; -import '../di_adapters/get_it_adapter.dart'; -import 'universal_chain_module.dart'; -import 'package:riverpod/riverpod.dart' as rp; - -/// Унифицированный generic-колбэк для регистрации зависимостей, -/// подходящий под выбранный DI-адаптер. -typedef Registration = void Function(TContainer); - -Registration getUniversalRegistration( - DIAdapter adapter, { - required int chainCount, - required int nestingDepth, - required UniversalBindingMode bindingMode, - required UniversalScenario scenario, -}) { - if (adapter is CherrypickDIAdapter) { - return (scope) { - scope.installModules([ - UniversalChainModule( - chainCount: chainCount, - nestingDepth: nestingDepth, - bindingMode: bindingMode, - scenario: scenario, - ), - ]); - } as Registration; - } - if (adapter is GetItAdapter) { - return (getIt) { - switch (scenario) { - case UniversalScenario.asyncChain: - for (int chain = 1; chain <= chainCount; chain++) { - for (int level = 1; level <= nestingDepth; level++) { - final prevDepName = '${chain}_${level - 1}'; - final depName = '${chain}_$level'; - getIt.registerSingletonAsync( - () async { - final prev = level > 1 - ? await getIt.getAsync(instanceName: prevDepName) - : null; - return UniversalServiceImpl(value: depName, dependency: prev as UniversalService?); - }, - instanceName: depName, - ); - } - } - break; - case UniversalScenario.register: - getIt.registerSingleton(UniversalServiceImpl(value: 'reg', dependency: null)); - break; - case UniversalScenario.named: - getIt.registerFactory(() => UniversalServiceImpl(value: 'impl1'), instanceName: 'impl1'); - getIt.registerFactory(() => UniversalServiceImpl(value: 'impl2'), instanceName: 'impl2'); - break; - case UniversalScenario.chain: - for (int chain = 1; chain <= chainCount; chain++) { - for (int level = 1; level <= nestingDepth; level++) { - final prevDepName = '${chain}_${level - 1}'; - final depName = '${chain}_$level'; - switch (bindingMode) { - case UniversalBindingMode.singletonStrategy: - getIt.registerSingleton( - UniversalServiceImpl( - value: depName, - dependency: level > 1 - ? getIt(instanceName: prevDepName) - : null, - ), - instanceName: depName, - ); - break; - case UniversalBindingMode.factoryStrategy: - getIt.registerFactory( - () => UniversalServiceImpl( - value: depName, - dependency: level > 1 - ? getIt(instanceName: prevDepName) - : null, - ), - instanceName: depName, - ); - break; - case UniversalBindingMode.asyncStrategy: - getIt.registerSingletonAsync( - () async => UniversalServiceImpl( - value: depName, - dependency: level > 1 - ? await getIt.getAsync(instanceName: prevDepName) - : null, - ), - instanceName: depName, - ); - break; - } - } - } - break; - case UniversalScenario.override: - // handled at benchmark level - break; - } - // UniversalService alias (без имени) для chain/override-сценариев - if (scenario == UniversalScenario.chain || scenario == UniversalScenario.override) { - final depName = '${chainCount}_$nestingDepth'; - getIt.registerSingleton( - getIt(instanceName: depName), - ); - } - } as Registration; - } - - if (adapter is DIAdapter>> && adapter.runtimeType.toString().contains('RiverpodAdapter')) { - return (providers) { - switch (scenario) { - case UniversalScenario.register: - providers['UniversalService'] = rp.Provider((ref) => UniversalServiceImpl(value: 'reg', dependency: null)); - break; - case UniversalScenario.named: - providers['impl1'] = rp.Provider((ref) => UniversalServiceImpl(value: 'impl1')); - providers['impl2'] = rp.Provider((ref) => UniversalServiceImpl(value: 'impl2')); - break; - case UniversalScenario.chain: - for (int chain = 1; chain <= chainCount; chain++) { - for (int level = 1; level <= nestingDepth; level++) { - final prevDepName = '${chain}_${level - 1}'; - final depName = '${chain}_$level'; - providers[depName] = rp.Provider((ref) => UniversalServiceImpl( - value: depName, - dependency: level > 1 ? ref.watch(providers[prevDepName] as rp.ProviderBase) : null, - )); - } - } - final depName = '${chainCount}_$nestingDepth'; - providers['UniversalService'] = rp.Provider((ref) => ref.watch(providers[depName] as rp.ProviderBase)); - break; - case UniversalScenario.override: - // handled at benchmark level - break; - case UniversalScenario.asyncChain: - for (int chain = 1; chain <= chainCount; chain++) { - for (int level = 1; level <= nestingDepth; level++) { - final prevDepName = '${chain}_${level - 1}'; - final depName = '${chain}_$level'; - providers[depName] = rp.FutureProvider((ref) async { - return UniversalServiceImpl( - value: depName, - dependency: level > 1 - ? await ref.watch(providers[prevDepName]!.future) as UniversalService? - : null, - ); - }); - } - } - final depName = '${chainCount}_$nestingDepth'; - providers['UniversalService'] = rp.FutureProvider((ref) async { - return await ref.watch(providers[depName]!.future) as UniversalService; - }); - break; - } - } as Registration; - } - - throw UnsupportedError('Unknown DIAdapter type: ${adapter.runtimeType}'); -}