具有 NullSafety 的 Flutter 中的单例 Class

Singleton Class in Flutter with NullSafety

我有这个class,它通过使用工厂构造函数接受一些参数,如果实例为空,将创建一个新对象;如果它不为 null,则实例的值将被返回,因此我们始终接收到相同的对象(Singleton)。这就是我在启用 dart 的空安全特性之前使用单例模式的方式。

class GuestUser extends User {
  static GeustUser _instance;


  factory GuestUser(
      {required String firstName,
      required String lastName,
      required String email,
      required Address address,
      required PaymentInfo paymentInfo}) {
    if (_instance == null) {
      _instance =
          GuestUser._(firstName, lastName, email, address, paymentInfo);
    }
    return _instance;
  }

现在启用空值安全,我收到此错误:

The non-nullable variable '_instance' must be initialized.
Try adding an initializer expression.

也不再需要 if (_instance == null)

如果我这样定义 _instance static late final GuestUser _instance; 那么我不能使用 if (_instance == null) 仅在需要时创建 _instance。所以每次调用工厂构造函数时我都必须删除 if 语句并创建一个新实例。

如何解决这个问题并创建一个启用空安全的单例 class?

我想到了这个解决方案来跟踪带有布尔变量的实例:

static late final GeustUser _instance;
static bool _isInstanceCreated = false;

  factory GuestUser(
      {required String firstName,
      required String lastName,
      required String email,
      required Address address,
      required PaymentInfo paymentInfo}) {
    if (_isInstanceCreated == false) {
      _instance =
          GuestUser._(firstName, lastName, email, address, paymentInfo);
    }
    _isInsanceCreated = true;
    return _instance;
  }

但我想知道是否有一种方法可以在不定义新变量的情况下使用 null-safety

你的单身人士有点奇怪,因为你接受的参数只会在第一次调用时使用。这不是一个很好的模式,因为它可以给一些惊喜,例如您想将它用于两个不同的来宾用户。

在您的示例中,仅使用 GuestUser? 作为 _instance 的类型是有意义的。在 Dart 中默认不可为空不应该被视为使用 null 是不好的,你绝对应该在有意义的地方使用 null(特别是如果我们可以防止引入 bool变量以指示是否已设置变量)。在您的示例中,_instance 在第一次初始化之前为 null。

我们使用 ??= 运算符的示例。运算符将检查 _instance 是否为 null,如果是 null,它将把变量分配给通过调用构造函数 GuestUser._ 创建的对象。此后它将 return 的值 _instance。如果 _instance 确实已经有一个值(不是 null),这个值将只是 returned 而不会调用 GuestUser._ 构造函数。

class GuestUser extends User {
  static GuestUser? _instance;

  factory GuestUser(
          {required String firstName,
          required String lastName,
          required String email,
          required Address address,
          required PaymentInfo paymentInfo}) =>
      _instance ??=
          GuestUser._(firstName, lastName, email, address, paymentInfo);
}

如果你有一个更传统的单例,你可以在定义中创建实例的地方创建一个 static final 变量。但这不允许参数。

正如提到的,你的单身人士实际上很奇怪,因为通常你会做这样的事情:

class Foo {
  static final instance = Foo._();
  Foo._();
}

但是,您不能用参数实例化它。为此,您可以这样做:

class Bar {
  static Bar? instance;
  Bar._(int i);

  factory Bar.fromInt(int i) {
    var bar = Bar._(i);
    instance = bar;
    return bar;
  }

  void display() => print('Bar instance');
}

void main() {
  final nullableBar = Bar.instance;
  final nonNullableBar = Bar.fromInt(0);

  nullableBar!.display(); // Runtime error as Bar.instance is used before Bar.fromMap(0)
  nonNullableBar.display(); // No error
}

您可以使用相同的传统方式创建单例。您只需将空安全运算符放在正确的位置即可。

class MySingleton {
  // make this nullable by adding '?'
  static MySingleton? _instance;

  MySingleton._() {
    // initialization and stuff
  }

  factory MySingleton() {
    if (_instance == null) {
      _instance = new MySingleton._();
    }
    // since you are sure you will return non-null value, add '!' operator
    return _instance!;
  }
}

你得到的是延迟初始化的一部分,使用这种方法我们在运行时创建实例,而不是在初始化时。

最后,使用 null 感知运算符将解决您的问题,最好使用工厂构造函数来初始化对象。

class GuestUser  {
  static GuestUser? _instance;

  GuestUser._( 
       String firstName,
       String lastName,
       String email,
       String address,
       String paymentInfo);

   factory GuestUser(
      {required String firstName,
      required String lastName,
      required String email,
      required String address,
      required String paymentInfo}) {
    _instance ??=
          GuestUser._(firstName, lastName, email, address, paymentInfo);
     return _instance!;
   }
  ///.. Rest code
}

像这样创建对象

  GuestUser user = GuestUser(firstName: "jitesh", lastName: "mohite", email: "jitesh@gmail.com", address: "Universe", paymentInfo: "As you like");