【问题标题】:Singleton Class in Flutter with NullSafety带有 NullSafety 的 Flutter 中的单例类
【发布时间】:2021-06-05 04:13:53
【问题描述】:

我有这个类,它通过使用工厂构造函数获取一些参数,如果实例为空,将创建一个新对象;如果它不为 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;
  }

现在启用 null-safety,我收到此错误:

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

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

【问题讨论】:

    标签: flutter dart dart-null-safety


    【解决方案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");
    

    【讨论】:

      【解决方案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!;
        }
      }
      

      【讨论】:

        【解决方案3】:

        正如@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
        }
        

        【讨论】:

        • 这很好,谢谢,但最好将地图传递给工厂,因为名称是 fromMap 但你传递的是一个 int。它当然可以,但在这个例子中使用地图更好。
        • @Morez 最初我打算使用Map(这是一般模式),但为了缩短代码我使用了int。糟糕,我刚刚注意到我在那里使用了fromMap 字。我实际上应该更改代码。哈哈。感谢您的关注!
        【解决方案4】:

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

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

        我们使用??= 运算符的示例。运算符将检查_instance 是否为null,在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-10-03
          • 2021-06-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-01-22
          • 2019-02-08
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多