Класс Singleton во флаттере с NullSafety

У меня есть этот класс, который принимает некоторые параметры с помощью конструктора фабрики, если instance имеет значение null, будет создан новый объект; если он не равен нулю, будет возвращено значение instance, поэтому мы всегда всегда получаем один и тот же объект (Singleton). Вот как я использовал шаблон 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.

И больше не нужен.

Если я определю _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;
  }

Но я хочу знать, есть ли способ сделать это без определения новой переменной и с использованием функций нулевой безопасности

5 ответов

Решение

Ваш синглтон немного странный, поскольку вы принимаете аргументы, которые будут использоваться только при первом вызове. Это не лучший шаблон, поскольку он может преподнести некоторые сюрпризы, если, например, вы захотите использовать его для двух разных гостевых пользователей.

В вашем примере имеет смысл просто использовать как тип для. Не допускающее значение NULL по умолчанию в Dart не должно рассматриваться, поскольку использование плохого, и вы обязательно должны использовать там, где это имеет смысл. В вашем примере имеет значение null, пока не будет инициализировано в первый раз.

Пример, в котором мы используем что позволяет очень просто обеспечить присваивается значение, только если оно :

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

Если бы у вас был более традиционный синглтон, вы могли бы сделать переменная, в которой создается экземпляр в определении. Но это не учитывает параметры.

Вы можете использовать тот же традиционный способ создания синглтона. Вам нужно только поместить оператор нулевой безопасности в нужное место.

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

Как упоминал @julemand101 , ваш синглтон на самом деле странный, потому что обычно вы делаете что-то вроде:

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

Однако вы не можете создать его с параметрами. Для этого вы можете сделать это:

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

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

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

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

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

То, что вы получаете, поскольку это часть ленивой инициализации, при таком подходе мы создаем экземпляр во время выполнения, а не во время инициализации.

Наконец, ваша проблема будет решена с помощью оператора с нулевым значением, и лучше использовать конструктор фабрики для инициализации объекта.

      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");

Что, если вы добавите переменную типа bool с false, если она не инициализирована? Просто проверьте его перед использованием и верните значение по умолчанию для вашего объекта. Кроме того, u может использовать динамическую вместо переменной определенного типа и возвращать false, если переменная пуста.

Другие вопросы по тегам