一个简单安全的 Dart 依赖注入(DI)解决方案,支持作用域。
简介
一个Pot 是一个创建并持有特定类型对象的容器。
- 简单
- 易于理解,因为它专注于 DI,没有其他功能。
- API 简单,您可以自信地知道如何使用。
- 将 Pot 作为全局变量易于处理,并能获得 IDE 的帮助。
- Safe
- 不依赖于类型。
- 无运行时错误。访问对象时对象始终存在。
- 也不依赖于字符串。
- 访问对象或作用域的按名称功能已故意排除。
- 更多类似的旨在安全的设计决策。
- 不依赖于类型。
政策
此包不会轻易采纳新功能,以保持其简洁性。
重点将更多地放在增强稳定性和健壮性上。
用法
使用工厂创建一个 Pot 来实例化一个对象。
一个 Pot 对应一个类型,这与其他大多数 DI 容器不同。
final counterPot = Pot(() => Counter(0));
现在,只要导入包含上述声明的文件,您就可以在任何需要的地方使用该 Pot。
请注意,除非有特殊原因,否则创建的 Pot 应分配给一个全局变量。
除非有特殊原因,否则不应如此。
获取对象
void someMethod() {
final counter = counterPot.get;
}
void someMethod() {
final counter = counterPot();
// Or
// final counter = counterPot.call();
}
创建对象
对象在首次访问时由工厂创建,如上所示。
如果您需要在不获取对象的情况下立即实例化一个对象,请使用create()
显式地。
void someMethod() {
counterPot.create();
}
丢弃对象
您可以通过多种方法(例如reset())来丢弃对象。如果对象在不再需要时得到妥善丢弃,资源就会得到保存。
如果对象被妥善丢弃,则会保存资源。
即使对象被丢弃,Pot 本身也不会被丢弃。当再次需要对象时,会创建一个新对象,因此不必担心对象可能已被丢弃而无法访问。
再次需要时,会创建一个新对象,因此不必担心对象可能已被丢弃而无法访问。
因此,不必担心对象可能已被丢弃而无法访问。
如果将回调函数传递给Pot 构造函数的 disposer,则在丢弃 Pot 中的对象时会调用该回调函数。将其用于执行与对象相关的清理工作。
它用于执行与对象相关的清理工作。
与对象相关的清理工作。
final counterPot = Pot<Counter>(
() => Counter(0),
disposer: (counter) => counter.dispose(),
);
void main() {
final counter = counterPot();
counter.increment();
...
// Discards the Counter object and triggers the disposer function.
counterPot.reset();
}
replace(), Pot.popScope(), Pot.resetAllInScope()
和Pot.resetAll() 也会丢弃现有对象。这些将在本文档的后续部分中进行解释。
将在本文档的后续部分中进行解释。
高级用法
替换对象工厂
如果 Pot 是由Pot.replaceable 创建的,则可以替换工厂。
否则,replace() 方法将不可用。
final userPot = Pot.replaceable(() => User.none());
Future<User> signIn() async {
final userId = await Auth.signIn(...);
userPot.replace(() => User(userId));
return userPot();
}
请注意,在替换工厂之前,现有对象会被丢弃,并且方式与手动使用reset() 时一样,它也会触发 disposer。
与手动使用 reset() 一样,它也会触发 disposer。
在测试中替换
如果替换工厂仅用于测试,请避免使用Pot.replaceable。
如果不必要,最好禁用它。
而是通过将Pot.forTesting 设置为 true 来启用替换,并使用 replaceForTesting()。
replaceForTesting()。.
final counterPot = Pot(() => Counter(0));
void main() {
Pot.forTesting = true;
test('Some test', () {
counterPot.replaceForTesting(() => Counter(100));
...
});
}
作用域
此包中的“作用域”是指 Pot 中保存的对象生命周期相关的概念。
它从 0 开始被赋予一个顺序号。添加作用域会增加当前作用域的索引号,移除作用域会减少它。
移除一个会减少它。
例如,如果在当前索引号为 0 时添加了作用域,则该数字变为 1。
如果此时创建了一个对象,它将被绑定到当前作用域 1。这意味着对象存在于当前作用域为 1 或更新的期间,因此当作用域 1 被移除时,它将被丢弃并触发 disposer。当前索引号将回到 0。
对象将被丢弃并触发 disposer。
当前索引号将回到 0。
final counterPot = Pot<Counter>(
() => Counter(),
disposer: (counter) => counter.dispose(),
);
void main() {
print(Pot.currentIndex); // 0
// At this point, the counter object is not bound to the scope 0
// because the object has not been created yet.
Pot.pushScope();
print(Pot.currentIndex); // 1
// The counter object is created here, and it is bound to scope 1.
final counter = counterPot();
// The scope 2 is removed and the counter object is discarded.
// In addition, the disposer is triggered.
Pot.popScope();
print(Pot.currentIndex); // 0
}
如果多个对象被绑定到当前作用域,您可以通过调用Pot.popScope() 来丢弃所有对象。
Pot.popScope() 也可以丢弃所有对象。
结合 replace() 和作用域
即使 Pot 最初是全局声明的,除非使用对象,否则不会创建对象。
因此,在对象仅从某个点开始使用的情况下,您可以最初使用虚拟工厂声明 Pot,然后在该点将其替换为实际的工厂,同时在那里添加作用域。
在添加作用域的同时,将其替换为实际的工厂。
示例代码(点击打开)
上面是使用 Flutter 的应用程序示例。
final todoDetailsPot = Pot<TodoDetails>(
// A dummy factory for the moment.
() => TodoDetails(),
disposer: (details) => details.dispose(),
);
class TodoDetailsPage extends StatefulWidget {
const TodoDetailsPage({required this.todoId});
final String todoId;
@override
_TodoDetailsPageState createState() => _TodoDetailsPageState();
}
class _TodoDetailsPageState extends State<TodoDetailsPage> {
@override
void initState() {
super.initState();
// A new scope is added, and the dummy factory is replaced with the actual one.
Pot.pushScope();
todoDetailsPot.replace(() => TodoDetails(widget.todoId));
}
@override
void dispose() {
// The TodoDetails object is discarded when the page is disposed of.
Pot.popScope();
super.dispose();
}
@override
Widget build(BuildContext context) {
// The TodoDetails object specified by the todo ID is obtained.
// It gets bound to the current scope when it is first accessed here.
final details = todoDetailsPot();
...
}
}
TodoDetails 对象仅在 TodoDetailsPage 中是必需的。
- 最好在页面中创建它,并在用户离开时将其丢弃。
- 它最好在页面中创建,并在用户离开时丢弃。
- 对象必须使用 todo ID 创建。
- 虚拟工厂被替换为使用 todo ID 的实际工厂。
在不移除作用域的情况下重置对象
Pot.resetAllInScope() 会丢弃绑定到当前作用域的所有对象,但作用域不会被移除。此方法可能只在极少数情况下需要。
此方法可能只在极少数情况下需要。
同样,Pot.resetAll() 会丢弃绑定到任何作用域的所有对象。
这对于重置所有对象以进行测试很有用。它也可能用于清除状态,使应用程序表现得好像重新启动一样。
这可能用于清除状态,使应用程序表现得好像重新启动一样。
每次重置对象的行为与reset() 相同,因此 disposer 的触发方式也相同。
因此,disposer 的触发方式也相同。
注意
所有 Pot 通常都应该全局声明,但也可以局部声明和使用 Pot,只要其对象得到妥善丢弃。即使 Pot 分配给局部变量,其部分数据也会全局存储,并且在变量不再使用时不会自动丢弃。因此,必须使用reset() 或具有相同效果的其他方法手动丢弃。
其部分数据会全局存储,即使 Pot 被分配给局部变量。
即使 Pot 被分配给局部变量,其部分数据也会全局存储。
因此,必须使用 reset() 或其他具有相同效果的方法手动丢弃。
reset() 或其他方法,它们具有相同效果。
void main() {
final myClass = MyClass();
...
myClass.dispose();
}
class MyClass {
final servicePot = Pot(() => MyService());
// Use reset() in a disposing method like this
// and make sure to call it at some point.
void dispose() {
servicePot.reset();
}
void someMethod() {
final service = servicePot();
...
}
}
在 Flutter 中,State 类的dispose() 方法或从中调用的方法是处理此问题的最理想位置。
是处理此问题的最理想位置。