8

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

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 语句并创建一个新实例。

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

我有这个解决方案来使用布尔变量跟踪实例:

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;
  }

但是我想知道是否有一种方法可以在不定义新变量并使用空安全特性的情况下做到这一点

4

4 回答 4

4

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

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

我们使用??=运算符的示例。操作员将检查是否_instancenull,如果是null,它会将变量分配给通过调用构造函数创建的对象GuestUser._。此后它将返回 的值_instance。如果_instance确实已经有一个值(不是null),那么这个值将被返回而不调用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在定义中创建实例的位置创建一个变量。但这不允许参数。

于 2021-03-06T20:30:37.347 回答
2

As @julemand101 mentioned, your singleton is actually strange, because generally you'd do something like:

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

However, you can't instantiate it with the parameters. For that you can do this:

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
}
于 2021-05-18T13:54:41.897 回答
2

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

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!;
  }
}
于 2021-06-09T09:52:10.983 回答
1

你得到的因为它是延迟初始化的一部分,通过这种方法,我们在运行时创建一个实例,而不是在初始化时。

最后,您的问题将使用 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");
于 2021-09-29T10:07:12.363 回答