状态管理和全球变量

我正在自学Flutter,之前学习的是传统的面向对象语言。我还是个初学者,但很明显状态管理是Flutter中的一个关键问题,所以我正在学习它(主要是用Providers)。

我所学的大部分内容似乎都是使用全局变量,从其他类中设置和调用(用 notifyListener() 在后一种情况下的调用)。) 但是当我学习OOP的时候,我被教导这是一件 "坏事"。一个对象可能会在无意中改变一个变量的值,破坏另一个对象。换句话说,封装是好的,全局变量是坏的--它们违反了封装的思想。

我错过了什么?

2
投票

提供者模型(或者更一般地说,订阅者-倾听者模型)不会打破封装。要想打破封装,一个对象的变化直接导致另一个对象的突变。例如,如果你有这两个类。

class A {
  int x;
  B b;
}

class B {
  String s;
  A a;
}

所以在这里我们有一个共同依赖关系 AB. 现在说有一种方法在 A 来改变其状态。

void changeState(int i) {
  this.x = i;
  b.s = i.toString();
}

这破坏了封装,因为 A 是直接改变状态的 B,这可能会导致一些功能被破坏,因为 B 试图用一个外部突变的状态来操作。

现在假设没有明确定义的共同依赖关系,而 AB 而是通过事件总线进行通信。

// A
void changeState(int i) {
  this.x = i;
  fireEvent('A-changed', this);
}

// B
listenToEvent<A>('A-changed', handleEvent);

...

void handleEvent(A source) {
  this.s = source.x.toString();
}

现在保持封装是因为 AB 正在交流它们的状态变化,而每个人只负责维护自己的状态。

这正是发生在具有 ChangeNotifier. 当一个对象更新其状态,然后调用 notifyListeners,Flutter使用内部事件总线来通知任何正在监听该对象的widget,无论是显式还是通过提供者的 Provider.of 或使用 Consumer. 该对象并不直接导致小组件重建,而是通过事件总线进行通信,并通知小组件应自行重建。这保留了封装,因为每个参与的对象只对自己的状态负责。

至于provider与全局变量有什么不同,那是因为provider使用了一种叫做 "依赖注入"(简称DI)的模式。通过DI,你可以把一个非全局对象 "注入 "到 "依赖 "它的widgets中。这最常见的是通过构造函数来完成的,即...。

class SomeService {
  Database db;

  SomeService(this.db);
}

在那个例子中 SomeService 类需要与数据库进行通信,但它没有调用某个全局数据库服务,而是采用了 Database 对象的创建时传递给它。这使得 SomeService 数据库进行通信,而无需依赖全局对象。这也允许你模拟 Database 对象进行测试。)

有了provider,它使用稍微不同的方法来实现DI。provider不使用构造函数,而是将资源嵌入到widget树中。从树上的那一点往下的小组件就可以动态地检索该资源,但在那一点之上或树的不同部分的小组件将无法访问它。这就是提供者实现DI的方式,也是它与全局变量的区别。