[英]Fight against circular dependency with get_it on flutter?
GetIt 是Dart 和 Flutter 项目的服务定位器( https://github.com/fluttercommunity/get_it )
我认为 GetIt 应该给我们一些关于循环依赖的细节。
例如,当我运行我的测试时,会有一个无限循环,它们永远不会结束。
get_it 用于定义全局对象和服务,并提供用于访问它们的定位器功能。 如果您的全局服务之间存在循环依赖关系,那么您很有可能将这些服务设计为彼此紧密耦合。 例如:
class ServiceA {
final ServiceB b;
ServiceA(this.b);
void foo() {
b.bar();
}
void bar() { ... }
}
class ServiceB {
final ServiceA a;
ServiceB(this.a);
void foo() {
a.bar();
}
void bar() { ... }
}
// GetIt Initialization
GetIt g = GetIt.instance;
g.registerLazySingleton<ServiceA>(() => ServiceA(g.get<ServiceB>()));
g.registerLazySingleton<ServiceB>(() => ServiceB(g.get<ServiceA>()));
在此之后,针对ServiceA
或ServiceB
调用GetIt.I.get
可能会创建由循环依赖引起的无限循环。 有了这个实现,没有真正的方法可以防止循环,而不会进入检查和延迟执行的兔子洞。
循环依赖的真正解决方案通常很简单:将您的应用设计为一开始就没有它。 例如,在上面的示例中, ServiceA
在构造函数中对ServiceB
具有刚性依赖,反之亦然。 相反,服务可以使用服务定位器在执行需要它的方法期间获取对必要服务的引用。 换句话说,我们已经在使用 get_it,为什么不在这里使用呢?
class ServiceA {
ServiceA();
void foo() {
final b = GetIt.I.get<ServiceB>();
b.bar();
}
void bar() { ... }
}
class ServiceB {
ServiceB();
void foo() {
final a = GetIt.I.get<ServiceA>();
a.bar();
}
}
// GetIt Initialization
GetIt g = GetIt.instance;
g.registerLazySingleton<ServiceA>(() => ServiceA());
g.registerLazySingleton<ServiceB>(() => ServiceB());
现在, ServiceA
和ServiceB
彼此之间的依赖关系更加动态,并且不依赖于它们各自的构造函数。 不再存在循环依赖问题,并且 get_it 初始化器在访问单例时将没有问题。
来解决这个问题。 这是此示例的部分解决方案:
一些接口和实现:
abstract class SuperLetter {}
abstract class A implements SuperLetter {}
class AC implements A {
final B b;
AC({this.b});
@override
String toString() {
if (b == null) {
return "Im an A.";
}
return "Im an A and my B is: $b";
}
}
abstract class B implements SuperLetter {}
class BC implements B {
final C c;
BC({this.c});
@override
String toString() {
if (c == null) {
return "Im a B.";
}
return "Im a B and my C: $c";
}
}
abstract class C implements SuperLetter {}
class CC implements C {
final SuperLetter a;
CC({this.a});
@override
String toString() {
if (a == null) {
return "Im a C.";
}
return "Im a C and my A: $a"; // might never be reached because of circular dependency
}
}
检测到循环依赖时抛出的新异常。
class CircularDependencyException implements Exception {
final wantedType;
final calledTypes;
CircularDependencyException({
this.wantedType,
this.calledTypes,
});
@override
String toString() {
String message = '🔺 Circular dependency ';
for (final e in calledTypes) {
message += '${e.toString()}';
if (e == calledTypes.last) {
message += ' -> $wantedType';
} else {
message += ' -> ';
}
}
return message;
}
}
对抗循环依赖:
class GetItProtector {
var _wantedType;
Set _calledTypes;
final GetIt getIt;
GetItProtector(this.getIt);
_check<T>() {
if (_wantedType == null) {
_wantedType = T;
_calledTypes = Set()..add(T);
} else {
if (_calledTypes.contains(T)) {
throw CircularDependencyException(wantedType: T, calledTypes: _calledTypes);
}
_calledTypes.add(T);
}
}
T call<T>() {
_check<T>();
final instance = getIt<T>();
if (instance.runtimeType == _wantedType) {
_wantedType = null;
_calledTypes.clear();
}
return instance;
}
}
A -> B -> C -> 循环依赖的示例:
void main() {
GetIt g = GetIt.instance;
final myGet = GetItProtector(g);
g.registerLazySingleton<A>(() => AC(
b: myGet(),
));
g.registerLazySingleton<B>(() => BC(
c: myGet(),
));
g.registerLazySingleton<C>(() => CC(
a: myGet<A>(),
));
A a = myGet();
print(a);
}
输出:
Error while creating C
Stack trace: ......
Error while creating B
Stack trace: ......
Error while creating A
Stack trace: ......
Unhandled exception:
🔺 Circular dependency A -> B -> C -> A
....
A -> B -> C -> B 的示例
void main() {
GetIt g = GetIt.instance;
final myGet = GetItProtector(g);
g.registerLazySingleton<A>(() => AC(
b: myGet(),
));
g.registerLazySingleton<B>(() => BC(
c: myGet(),
));
g.registerLazySingleton<C>(() => CC(
a: myGet<B>(),
));
A a = myGet();
print(a);
}
输出
Error while creating C
Stack trace: ......
Error while creating B
Stack trace: ......
Error while creating A
Stack trace: ......
Unhandled exception:
🔺 Circular dependency A -> B -> C -> B
....
在这个解决方案中,我不测试类型是否是真实类型而不是对象或动态。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.