2025-06-13 16:50:27 +03:00
|
|
|
|
import 'package:cherrypick/cherrypick.dart';
|
|
|
|
|
|
|
|
|
|
|
|
// Пример сервисов с циклической зависимостью
|
|
|
|
|
|
class UserService {
|
|
|
|
|
|
final OrderService orderService;
|
|
|
|
|
|
|
|
|
|
|
|
UserService(this.orderService);
|
|
|
|
|
|
|
|
|
|
|
|
void createUser(String name) {
|
|
|
|
|
|
print('Creating user: $name');
|
|
|
|
|
|
// Пытаемся получить заказы пользователя, что создает циклическую зависимость
|
|
|
|
|
|
orderService.getOrdersForUser(name);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class OrderService {
|
|
|
|
|
|
final UserService userService;
|
|
|
|
|
|
|
|
|
|
|
|
OrderService(this.userService);
|
|
|
|
|
|
|
|
|
|
|
|
void getOrdersForUser(String userName) {
|
|
|
|
|
|
print('Getting orders for user: $userName');
|
|
|
|
|
|
// Пытаемся получить информацию о пользователе, что создает циклическую зависимость
|
|
|
|
|
|
userService.createUser(userName);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Модули с циклическими зависимостями
|
|
|
|
|
|
class UserModule extends Module {
|
|
|
|
|
|
@override
|
|
|
|
|
|
void builder(Scope currentScope) {
|
|
|
|
|
|
bind<UserService>().toProvide(() => UserService(
|
|
|
|
|
|
currentScope.resolve<OrderService>()
|
|
|
|
|
|
));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class OrderModule extends Module {
|
|
|
|
|
|
@override
|
|
|
|
|
|
void builder(Scope currentScope) {
|
|
|
|
|
|
bind<OrderService>().toProvide(() => OrderService(
|
|
|
|
|
|
currentScope.resolve<UserService>()
|
|
|
|
|
|
));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Правильная реализация без циклических зависимостей
|
|
|
|
|
|
class UserRepository {
|
|
|
|
|
|
void createUser(String name) {
|
|
|
|
|
|
print('Creating user in repository: $name');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
String getUserInfo(String name) {
|
|
|
|
|
|
return 'User info for: $name';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class OrderRepository {
|
|
|
|
|
|
void createOrder(String orderId, String userName) {
|
|
|
|
|
|
print('Creating order $orderId for user: $userName');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
List<String> getOrdersForUser(String userName) {
|
|
|
|
|
|
return ['order1', 'order2', 'order3'];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class ImprovedUserService {
|
|
|
|
|
|
final UserRepository userRepository;
|
|
|
|
|
|
|
|
|
|
|
|
ImprovedUserService(this.userRepository);
|
|
|
|
|
|
|
|
|
|
|
|
void createUser(String name) {
|
|
|
|
|
|
userRepository.createUser(name);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
String getUserInfo(String name) {
|
|
|
|
|
|
return userRepository.getUserInfo(name);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class ImprovedOrderService {
|
|
|
|
|
|
final OrderRepository orderRepository;
|
|
|
|
|
|
final ImprovedUserService userService;
|
|
|
|
|
|
|
|
|
|
|
|
ImprovedOrderService(this.orderRepository, this.userService);
|
|
|
|
|
|
|
|
|
|
|
|
void createOrder(String orderId, String userName) {
|
|
|
|
|
|
// Проверяем, что пользователь существует
|
|
|
|
|
|
final userInfo = userService.getUserInfo(userName);
|
|
|
|
|
|
print('User exists: $userInfo');
|
|
|
|
|
|
|
|
|
|
|
|
orderRepository.createOrder(orderId, userName);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
List<String> getOrdersForUser(String userName) {
|
|
|
|
|
|
return orderRepository.getOrdersForUser(userName);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Правильные модули без циклических зависимостей
|
|
|
|
|
|
class ImprovedUserModule extends Module {
|
|
|
|
|
|
@override
|
|
|
|
|
|
void builder(Scope currentScope) {
|
|
|
|
|
|
bind<UserRepository>().singleton().toProvide(() => UserRepository());
|
|
|
|
|
|
bind<ImprovedUserService>().toProvide(() => ImprovedUserService(
|
|
|
|
|
|
currentScope.resolve<UserRepository>()
|
|
|
|
|
|
));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class ImprovedOrderModule extends Module {
|
|
|
|
|
|
@override
|
|
|
|
|
|
void builder(Scope currentScope) {
|
|
|
|
|
|
bind<OrderRepository>().singleton().toProvide(() => OrderRepository());
|
|
|
|
|
|
bind<ImprovedOrderService>().toProvide(() => ImprovedOrderService(
|
|
|
|
|
|
currentScope.resolve<OrderRepository>(),
|
|
|
|
|
|
currentScope.resolve<ImprovedUserService>()
|
|
|
|
|
|
));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
|
|
print('=== Circular Dependency Detection Example ===\n');
|
|
|
|
|
|
|
|
|
|
|
|
// Example 1: Demonstrate circular dependency
|
|
|
|
|
|
print('1. Attempt to create a scope with circular dependencies:');
|
|
|
|
|
|
try {
|
2025-08-08 11:23:14 +03:00
|
|
|
|
final scope = CherryPick.openRootScope();
|
2025-06-13 16:50:27 +03:00
|
|
|
|
scope.enableCycleDetection(); // Включаем обнаружение циклических зависимостей
|
|
|
|
|
|
|
|
|
|
|
|
scope.installModules([
|
|
|
|
|
|
UserModule(),
|
|
|
|
|
|
OrderModule(),
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
// Это должно выбросить CircularDependencyException
|
|
|
|
|
|
final userService = scope.resolve<UserService>();
|
|
|
|
|
|
print('UserService created: $userService');
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
print('❌ Circular dependency detected: $e\n');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Example 2: Without circular dependency detection (dangerous!)
|
|
|
|
|
|
print('2. Same code without circular dependency detection:');
|
|
|
|
|
|
try {
|
2025-08-08 11:23:14 +03:00
|
|
|
|
final scope = CherryPick.openRootScope();
|
2025-06-13 16:50:27 +03:00
|
|
|
|
// НЕ включаем обнаружение циклических зависимостей
|
|
|
|
|
|
|
|
|
|
|
|
scope.installModules([
|
|
|
|
|
|
UserModule(),
|
|
|
|
|
|
OrderModule(),
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
// Это приведет к StackOverflowError при попытке использования
|
|
|
|
|
|
final userService = scope.resolve<UserService>();
|
|
|
|
|
|
print('UserService создан: $userService');
|
|
|
|
|
|
|
|
|
|
|
|
// Попытка использовать сервис приведет к бесконечной рекурсии
|
|
|
|
|
|
// userService.createUser('John'); // Раскомментируйте для демонстрации StackOverflow
|
|
|
|
|
|
print('⚠️ UserService created, but using it will cause StackOverflow\n');
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
print('❌ Error: $e\n');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Example 3: Correct architecture without circular dependencies
|
|
|
|
|
|
print('3. Correct architecture without circular dependencies:');
|
|
|
|
|
|
try {
|
2025-08-08 11:23:14 +03:00
|
|
|
|
final scope = CherryPick.openRootScope();
|
2025-06-13 16:50:27 +03:00
|
|
|
|
scope.enableCycleDetection(); // Включаем для безопасности
|
|
|
|
|
|
|
|
|
|
|
|
scope.installModules([
|
|
|
|
|
|
ImprovedUserModule(),
|
|
|
|
|
|
ImprovedOrderModule(),
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
final userService = scope.resolve<ImprovedUserService>();
|
|
|
|
|
|
final orderService = scope.resolve<ImprovedOrderService>();
|
|
|
|
|
|
|
|
|
|
|
|
print('✅ Services created successfully');
|
|
|
|
|
|
|
|
|
|
|
|
// Демонстрация работы
|
|
|
|
|
|
userService.createUser('John');
|
|
|
|
|
|
orderService.createOrder('ORD-001', 'John');
|
|
|
|
|
|
final orders = orderService.getOrdersForUser('John');
|
|
|
|
|
|
print('✅ Orders for user John: $orders');
|
|
|
|
|
|
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
print('❌ Error: $e');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
print('\n=== Recommendations ===');
|
|
|
|
|
|
print('1. Always enable circular dependency detection in development mode.');
|
|
|
|
|
|
print('2. Use repositories and services to separate concerns.');
|
|
|
|
|
|
print('3. Avoid mutual dependencies between services at the same level.');
|
|
|
|
|
|
print('4. Use events or mediators to decouple components.');
|
|
|
|
|
|
}
|