状态管理和全局变量
State Management and Global Variables
我正在自学 Flutter,之前学习过传统的面向对象语言。我仍然是初学者,但很明显状态管理是 Flutter 中的一个关键问题,所以我正在学习它(主要是通过 Providers)。
我正在学习的大部分内容似乎都在使用全局变量,从其他 类 设置和调用(在后一种情况下使用 notifyListener()
调用)。但是当我了解 OOP 时,我被告知这是一件 "bad" 的事情。一个对象可能会无意中更改变量的值,从而破坏另一个对象。换句话说,封装好,全局变量不好——违反了封装的思想。
我错过了什么?
提供者模型(或更一般地说,订阅者-侦听器模型)不会破坏封装。对于要打破的封装,一个对象的变化直接导致另一个对象的突变。例如,如果你有这两个 classes:
class A {
int x;
B b;
}
class B {
String s;
A a;
}
所以这里我们在 A
和 B
之间有一个相互依赖关系。现在说 A
中有一个方法来改变它的状态:
void changeState(int i) {
this.x = i;
b.s = i.toString();
}
这会破坏封装,因为 A
正在直接更改 B
的状态,这可能会导致某些功能损坏,因为 B
尝试使用外部变异状态进行操作。
现在说没有明确定义的相互依赖关系,A
和 B
而是通过事件总线进行通信:
// 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();
}
现在保持封装是因为 A
和 B
正在传达它们的状态变化,每个人只负责维护自己的状态。
这正是 ChangeNotifier
提供程序中发生的情况。当对象更新其状态然后调用 notifyListeners
时,Flutter 使用内部事件总线通知任何正在侦听该对象的小部件,无论是显式或通过提供者的 Provider.of
或使用 Consumer
.该对象不会直接导致小部件重建,而是通过事件总线进行通信并通知小部件它应该自行重建。这保留了封装,因为所涉及的每个对象只对自己的状态负责。
至于provider与全局变量有何不同,那是因为provider使用了一种叫做"dependency injection"(简称DI)的模式。使用 DI,您可以将非全局对象 "inject" 放入其上 "depend" 的小部件中。这通常是通过构造函数完成的,即:
class SomeService {
Database db;
SomeService(this.db);
}
在该示例中,SomeService
class 需要与数据库通信,但它没有调用某些全局数据库服务,而是将 Database
对象传递给它创建。这为 SomeService
提供了一个无需依赖全局对象即可与之通信的数据库。 (这也允许您出于测试目的模拟 Database
对象。)
对于提供程序,它使用稍微不同的方法实现 DI。提供者不使用构造函数,而是将资源嵌入到小部件树中。树中该点向下的小部件将能够动态检索该资源,但该点上方或树的不同部分中的小部件将无法访问它。这就是 provider 实现 DI 的方式,也是它与全局变量的区别。
我正在自学 Flutter,之前学习过传统的面向对象语言。我仍然是初学者,但很明显状态管理是 Flutter 中的一个关键问题,所以我正在学习它(主要是通过 Providers)。
我正在学习的大部分内容似乎都在使用全局变量,从其他 类 设置和调用(在后一种情况下使用 notifyListener()
调用)。但是当我了解 OOP 时,我被告知这是一件 "bad" 的事情。一个对象可能会无意中更改变量的值,从而破坏另一个对象。换句话说,封装好,全局变量不好——违反了封装的思想。
我错过了什么?
提供者模型(或更一般地说,订阅者-侦听器模型)不会破坏封装。对于要打破的封装,一个对象的变化直接导致另一个对象的突变。例如,如果你有这两个 classes:
class A {
int x;
B b;
}
class B {
String s;
A a;
}
所以这里我们在 A
和 B
之间有一个相互依赖关系。现在说 A
中有一个方法来改变它的状态:
void changeState(int i) {
this.x = i;
b.s = i.toString();
}
这会破坏封装,因为 A
正在直接更改 B
的状态,这可能会导致某些功能损坏,因为 B
尝试使用外部变异状态进行操作。
现在说没有明确定义的相互依赖关系,A
和 B
而是通过事件总线进行通信:
// 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();
}
现在保持封装是因为 A
和 B
正在传达它们的状态变化,每个人只负责维护自己的状态。
这正是 ChangeNotifier
提供程序中发生的情况。当对象更新其状态然后调用 notifyListeners
时,Flutter 使用内部事件总线通知任何正在侦听该对象的小部件,无论是显式或通过提供者的 Provider.of
或使用 Consumer
.该对象不会直接导致小部件重建,而是通过事件总线进行通信并通知小部件它应该自行重建。这保留了封装,因为所涉及的每个对象只对自己的状态负责。
至于provider与全局变量有何不同,那是因为provider使用了一种叫做"dependency injection"(简称DI)的模式。使用 DI,您可以将非全局对象 "inject" 放入其上 "depend" 的小部件中。这通常是通过构造函数完成的,即:
class SomeService {
Database db;
SomeService(this.db);
}
在该示例中,SomeService
class 需要与数据库通信,但它没有调用某些全局数据库服务,而是将 Database
对象传递给它创建。这为 SomeService
提供了一个无需依赖全局对象即可与之通信的数据库。 (这也允许您出于测试目的模拟 Database
对象。)
对于提供程序,它使用稍微不同的方法实现 DI。提供者不使用构造函数,而是将资源嵌入到小部件树中。树中该点向下的小部件将能够动态检索该资源,但该点上方或树的不同部分中的小部件将无法访问它。这就是 provider 实现 DI 的方式,也是它与全局变量的区别。