Flutter中的单例类与NullSafety

10

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

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

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

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

请在评论中告诉我如何调用。 - Innocent
@Innocent,你可以简单地执行 foo = MySingleton() 来创建该类的实例。 - bikram

4

你的单例模式有点奇怪,因为它接受的参数只会在第一次调用时使用。这不是一个很好的模式,因为如果您想将其用于两个不同的访客用户,可能会产生一些意外。

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

以下是使用??=运算符的示例。运算符将检查_instance是否为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变量,在定义中创建实例。但是这样做无法接受参数。

2
正如@julemand101所提到的,你的单例实际上很奇怪,因为通常你会这样做:
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
}

这很好,谢谢。但最好将一个 map 传递给工厂,因为名称是 fromMap,但你正在传递一个 int。当然它可以工作,但在这个例子中使用 map 更好。 - Morez
1
@Morez 起初我打算使用Map(这是一般的模式),但为了让代码更简短,我使用了int。哎呀,我刚注意到我在那里使用了fromMap这个单词。我应该改变代码。哈哈。谢谢你的发现! - CopsOnRoad

1
作为Lazy Initialization的一部分,您获得的是在运行时创建实例而不是在初始化时创建实例的方法。
最后,您可以使用null aware operator解决您的问题,并最好使用工厂构造函数来初始化对象。
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");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接