【问题标题】:State Management and Global Variables状态管理和全局变量
【发布时间】:2020-08-24 07:32:33
【问题描述】:

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

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

我错过了什么?

【问题讨论】:

  • 不要认为你缺少任何东西。目前,Flutter 状态管理有点像一门黑暗的艺术,希望它在某个时候结晶。我使用 scoped_model ,因为我的应用程序是 12 个月前首次开发的,但由于 scoped_model 已被弃用,我将转向 Provider。我也使用全局变量,但通常它们是常量或仅在启动应用程序时设置一次(我还没有意识到单例)。
  • 全局变量就是这样 - 全局的。当您使用提供者时,您正在使用依赖注入为小部件提供对可能是也可能不是全局的对象的引用(并且不应该是,因为这违背了提供者的目的)。就“打破封装”而言,情况并非如此,因为源正在通知目标其状态已更改,这反过来又会导致目标对象在必要时更新其自己的状态。这与一个对象直接改变另一个对象的属性不同,因为每个对象仍然负责自己的状态。
  • @GrahamD 在启动时设置一次的非常量全局值正是单例。
  • @Abion47 感谢您确认它的想法。赞赏。我认为我在单例语法/术语而不是概念上更加挣扎。希望能到达那里。
  • @GrahamD 单例没有单一的语法或实现(尽管约定它是作为具有静态instance 属性的类实现的)。严格来说,单例只是一个全局变量,它被赋值一次,然后在程序的生命周期中被多次引用。除此之外的一切都只是实现细节。

标签: flutter dart global-variables state-management


【解决方案1】:

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

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 使用内部事件总线来通知正在侦听该对象的任何小部件,无论是显式地还是通过提供者的Provider.of 或使用Consumer。该对象不直接导致小部件重建,而是通过事件总线进行通信并通知小部件它应该重建自己。这保留了封装性,因为所涉及的每个对象只负责自己的状态。


provider 与全局变量有何不同,这是因为 provider 使用了一种称为“依赖注入”(或简称 DI)的模式。使用 DI,您可以获取一个非全局对象并将其“注入”到“依赖”它的小部件中。这通常通过构造函数完成,即:

class SomeService {
  Database db;

  SomeService(this.db);
}

在该示例中,SomeService 类需要与数据库通信,但不是调用某个全局数据库服务,而是在创建时将Database 对象传递给它。这为SomeService 提供了一个无需依赖全局对象即可与之通信的数据库。 (这也允许您模拟 Database 对象以进行测试。)

通过提供程序,它使用稍微不同的方法实现 DI。提供者不使用构造函数,而是将资源嵌入到小部件树中。然后从树中该点向下的小部件将能够动态检索该资源,但在该点上方或树的不同部分中的小部件将无法访问它。这就是提供者实现 DI 的方式,并将其与全局变量区分开来。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-28
    • 2020-12-28
    • 1970-01-01
    • 1970-01-01
    • 2020-09-29
    • 1970-01-01
    相关资源
    最近更新 更多