Pub Version
Dart CI

一个简单安全的 Dart 依赖注入(DI)解决方案,支持作用域。

简介

一个Pot 是一个创建并持有特定类型对象的容器。

  • 简单
    • 易于理解,因为它专注于 DI,没有其他功能。
    • API 简单,您可以自信地知道如何使用。
    • 将 Pot 作为全局变量易于处理,并能获得 IDE 的帮助。
  • Safe
    • 不依赖于类型。
      • 无运行时错误。访问对象时对象始终存在。
    • 也不依赖于字符串。
      • 访问对象或作用域的按名称功能已故意排除。
      • 更多类似的旨在安全的设计决策。

政策

此包不会轻易采纳新功能,以保持其简洁性。
重点将更多地放在增强稳定性和健壮性上。

用法

使用工厂创建一个 Pot 来实例化一个对象。

一个 Pot 对应一个类型,这与其他大多数 DI 容器不同。

final counterPot = Pot(() => Counter(0));

现在,只要导入包含上述声明的文件,您就可以在任何需要的地方使用该 Pot。

请注意,除非有特殊原因,否则创建的 Pot 应分配给一个全局变量。
除非有特殊原因,否则不应如此。

获取对象

您可以使用getcall()

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() 方法或从中调用的方法是处理此问题的最理想位置。
是处理此问题的最理想位置。

GitHub

查看 Github