你如何在Dart中建立一个单身人士?

Set*_*add 140 singleton dart

单例模式确保只创建一个类的一个实例.我如何在Dart中构建它?

Set*_*add 219

感谢Dart的工厂构造函数,构建单例很容易:

class Singleton {
  static final Singleton _singleton = Singleton._internal();

  factory Singleton() {
    return _singleton;
  }

  Singleton._internal();
}
Run Code Online (Sandbox Code Playgroud)

你可以用它来构建它 new

main() {
  var s1 = Singleton();
  var s2 = Singleton();
  print(identical(s1, s2));  // true
  print(s1 == s2);           // true
}
Run Code Online (Sandbox Code Playgroud)

  • 我没有实例化它两次,只是两次引用Singleton对象.你可能不会在现实生活中连续两次这样做:)我不想要抛出异常,每次我说"new Singleton()"时我只想要相同的单例实例.我承认,这有点令人困惑......"new"并不意味着"构建一个新的",它只是说"运行构造函数". (35认同)
  • @SethLadd这是非常好的,但我建议它需要几点解释.有一种奇怪的语法`Singleton._internal();`当它真的是一个构造函数定义时看起来像一个方法调用.有'_internal`这个名字.还有一个漂亮的语言设计点Dart允许你使用普通的构造函数开始(dart out?)然后,如果需要,将其更改为`factory`方法而不更改所有调用者. (6认同)
  • 您使用构造函数来获取实例会让您感到困惑.`new`关键字表示该类是实例化的,而不是.我会像在Java中那样使用静态方法`get()`或`getInstance()`. (3认同)
  • 尽管实例化两次有什么意义?如果在第二次实例化它时引发了错误,那不是更好吗? (2认同)
  • 工厂关键字在这里到底有什么作用?它纯粹是注释实现。为什么需要它? (2认同)
  • @Ganymede如果单例的构造函数是常量,那么您也可以让单例实例也保持常量。只需要知道,为了做到这一点,类不能有任何状态。(即所有字段都必须标记为 Final 并在构造函数中或仅使用其他常量的声明时进行初始化。) (2认同)

Sur*_*gch 66

这是在Dart中创建单例的几种不同方式的比较。

1.工厂构造函数

class SingletonOne {

  SingletonOne._privateConstructor();

  static final SingletonOne _instance = SingletonOne._privateConstructor();

  factory SingletonOne(){
    return _instance;
  }

}
Run Code Online (Sandbox Code Playgroud)

2.带有吸气剂的静态场

class SingletonTwo {

  SingletonTwo._privateConstructor();

  static final SingletonTwo _instance = SingletonTwo._privateConstructor();

  static SingletonTwo get instance { return _instance;}

}
Run Code Online (Sandbox Code Playgroud)

3.静态场

class SingletonThree {

  SingletonThree._privateConstructor();

  static final SingletonThree instance = SingletonThree._privateConstructor();

}
Run Code Online (Sandbox Code Playgroud)

如何实例化

上面的单例是这样实例化的:

SingletonOne one = SingletonOne();
SingletonTwo two = SingletonTwo.instance;
SingletonThree three = SingletonThree.instance;
Run Code Online (Sandbox Code Playgroud)

注意:

我最初将其作为一个问题,但发现上述所有方法均有效,并且选择很大程度上取决于个人喜好。

  • 我只是赞成你的回答。比公认的答案清楚得多。还有一个问题:对于第二种和第三种方式,私有构造函数的意义是什么?我看到很多人这样做,但我不明白这一点。我总是简单地使用`static final SingletonThree instance = SingletonThree()`。`_instance` 的第二种方式也是如此。我不知道不使用私有构造函数有什么缺点。到目前为止,我没有发现任何问题。无论如何,第二种和第三种方法都不会阻止对默认构造函数的调用。 (4认同)
  • @ sgon00,私有构造函数是这样,因此您无法创建另一个实例。否则任何人都可以执行“ SingletonThree instance2 = SingletonThree()”。如果在有私有构造函数的情况下尝试执行此操作,则会收到错误:`类'SingletonThree'没有默认的构造函数。 (2认同)
  • “2”方式与“3”方式的目的是什么?它的作用相同,但无缘无故地增加了冗长。为什么要分开吸气剂? (2认同)
  • 这个答案很棒。谢谢。对于那些想知道选择哪种方式的人,这是我的意见。我更喜欢方法 3,原因如下。在方法 1 中,您会得到一个像这样的实例 `final serviceInstance = Service()` 在我看来,使用现有实例并不明显。所以我更喜欢`final serviceInstance = Service.instance`,这使得它更加明确。 (2认同)

Gre*_*owe 34

我发现阅读新的Singleton()并不是非常直观.您必须阅读文档才能知道new实际上并没有像通常那样创建新实例.

这是做单身人士的另一种方式(基本上是安德鲁所说的).

LIB/thing.dart

library thing;

final Thing thing = new Thing._private();

class Thing {
   Thing._private() { print('#2'); }
   foo() {
     print('#3');
   }
}
Run Code Online (Sandbox Code Playgroud)

main.dart

import 'package:thing/thing.dart';

main() {
  print('#1');
  thing.foo();
}
Run Code Online (Sandbox Code Playgroud)

请注意,由于Dart的延迟初始化,直到第一次调用getter时才会创建单例.

如果你愿意,你也可以在单例类上实现单例作为静态getter.即Thing.Singleton,而不是顶级的getter.

还阅读了Bob Nystrom 从他的游戏编程模式书中单身人士的看法.


Ham*_*med 22

这是一个简单的答案:

首先,我们需要一个privatestatic类类型的属性。

其次,构造函数应该是private,因为我们要防止从类外部初始化对象。

最后,我们检查实例的可空性,如果它为空,我们将实例化并返回它,否则我们将返回已经实例化的实例。

延迟加载的实现

class Singleton {
  static Singleton? _instance;

  Singleton._();

  static Singleton get instance => _instance ??= Singleton._();

  ...
}
Run Code Online (Sandbox Code Playgroud)

使用 Eager Loading 实现

class Singleton {
  static Singleton _instance = Singleton._();

  Singleton._();

  static Singleton get instance => _instance;

  ...
}
Run Code Online (Sandbox Code Playgroud)

用法

Singleton.instance.someMethod();
Run Code Online (Sandbox Code Playgroud)

  • 这里发生了什么事?一个解释可以为你赢得更多积分 (4认同)

小智 12

如何在库中使用全局变量呢?

single.dart:

library singleton;

var Singleton = new Impl();

class Impl {
  int i;
}
Run Code Online (Sandbox Code Playgroud)

main.dart:

import 'single.dart';

void main() {
  var a = Singleton;
  var b = Singleton;
  a.i = 2;
  print(b.i);
}
Run Code Online (Sandbox Code Playgroud)

或者这不赞成?

单例模式在Java中是必要的,其中全局变量的概念不存在,但似乎你不需要在Dart中走很长的路.

  • 顶级变量很酷.但是,任何可以导入single.dart的人都可以自由地构造一个"new Impl()".你可以给Impl一个下划线构造函数,但是然后代码*里面*单例库可以调用那个构造函数. (4认同)
  • 嗨@Jan,它不是更好或更糟,它只是不同.在Andrew的例子中,Impl不是单例类.他确实使用了顶级变量来使单例`Singleton`易于访问.在上面的例子中,`Singleton`类是一个真正的单例,在孤立中只能存在一个`Singleton`实例. (2认同)
  • 赛斯,你说得不对。在 Dart 中没有_没有_方法可以构建真正的单例,因为无法限制类 _inside_ 声明库的实例化。它总是需要图书馆作者的纪律。在您的示例中,声明库可以根据需要多次调用 `new Singleton._internal()`,从而创建许多 `Singleton` 类的对象。如果 Andrew 示例中的 `Impl` 类是私有的(`_Impl`),它就会与您的示例相同。另一方面,单例是一种反模式,无论如何都不应该使用它。 (2认同)
  • @Ladicek,您不相信库的开发人员不会调用新的“Singelton._internal()”。您可以争辩说,singelton 类的开发人员也可以多次实例化该类。当然有枚举singelton,但对我来说它仅具有理论上的用途。枚举是枚举,不是单例...至于顶级变量的使用(@Andrew和@Seth):没有人可以写入顶级变量吗?它根本没有受到保护,或者我错过了什么? (2认同)

小智 9

由const构造函数和工厂执行dart singleton

class Singleton {
  factory Singleton() =>
    const Singleton._internal_();
  const Singleton._internal_();
}


void main() {
  print(new Singleton() == new Singleton());
  print(identical(new Singleton() , new Singleton()));
}
Run Code Online (Sandbox Code Playgroud)

  • 您好,此代码在 DartPad.dev 中打印 2 x `false`。在返回新实例之前,需要对该实例进行 null 检查。 (2认同)

iBo*_*101 9

这是另一种可能的方式:

void main() {
  var s1 = Singleton.instance;
  s1.somedata = 123;
  var s2 = Singleton.instance;
  print(s2.somedata); // 123
  print(identical(s1, s2));  // true
  print(s1 == s2); // true
  //var s3 = new Singleton(); //produces a warning re missing default constructor and breaks on execution
}

class Singleton {
  static final Singleton _singleton = new Singleton._internal();
  Singleton._internal();
  static Singleton get instance => _singleton;
  var somedata;
}
Run Code Online (Sandbox Code Playgroud)


Jac*_*ips 9

这是一个结合了其他解决方案的简洁示例。访问单例可以通过以下方式完成:

  • 使用singleton指向实例的全局变量。
  • 常见的Singleton.instance模式。
  • 使用默认构造函数,它是返回实例的工厂。

注意:您应该仅实现三个选项之一,以便使用单例的代码保持一致。

Singleton get singleton => Singleton.instance;
ComplexSingleton get complexSingleton => ComplexSingleton._instance;

class Singleton {
  static final Singleton instance = Singleton._private();
  Singleton._private();
  factory Singleton() => instance;
}

class ComplexSingleton {
  static ComplexSingleton _instance;
  static ComplexSingleton get instance => _instance;
  static void init(arg) => _instance ??= ComplexSingleton._init(arg);

  final property;
  ComplexSingleton._init(this.property);
  factory ComplexSingleton() => _instance;
}
Run Code Online (Sandbox Code Playgroud)

如果您需要进行复杂的初始化,则只需在稍后在程序中使用实例之前执行此操作即可。

例子

void main() {
  print(identical(singleton, Singleton.instance));        // true
  print(identical(singleton, Singleton()));               // true
  print(complexSingleton == null);                        // true
  ComplexSingleton.init(0); 
  print(complexSingleton == null);                        // false
  print(identical(complexSingleton, ComplexSingleton())); // true
}
Run Code Online (Sandbox Code Playgroud)


Luc*_*ach 9

实例化后不能改变对象的单例

class User {
  final int age;
  final String name;
  
  User({
    this.name,
    this.age
    });
  
  static User _instance;
  
  static User getInstance({name, age}) {
     if(_instance == null) {
       _instance = User(name: name, age: age);
       return _instance;
     }
    return _instance;
  }
}

  print(User.getInstance(name: "baidu", age: 24).age); //24
  
  print(User.getInstance(name: "baidu 2").name); // is not changed //baidu

  print(User.getInstance()); // {name: "baidu": age 24}
Run Code Online (Sandbox Code Playgroud)


sul*_*rza 9

这就是我在项目中实现单例的方式

灵感来自 flutter firebase =>FirebaseFirestore.instance.collection('collectionName')

class FooAPI {
  foo() {
    // some async func to api
  }
}

class SingletonService {
  FooAPI _fooAPI;

  static final SingletonService _instance = SingletonService._internal();

  static SingletonService instance = SingletonService();

  factory SingletonService() {
    return _instance;
  }

  SingletonService._internal() {
    // TODO: add init logic if needed
    // FOR EXAMPLE API parameters
  }

  void foo() async {
    await _fooAPI.foo();
  }
}

void main(){
  SingletonService.instance.foo();
}
Run Code Online (Sandbox Code Playgroud)

我的项目的例子

class FirebaseLessonRepository implements LessonRepository {
  FirebaseLessonRepository._internal();

  static final _instance = FirebaseLessonRepository._internal();

  static final instance = FirebaseLessonRepository();

  factory FirebaseLessonRepository() => _instance;

  var lessonsCollection = fb.firestore().collection('lessons');
  
  // ... other code for crud etc ...
}

// then in my widgets
FirebaseLessonRepository.instance.someMethod(someParams);
Run Code Online (Sandbox Code Playgroud)


Max*_*lin 8

如果您碰巧使用 Flutter 和providerpackage 进行状态管理,那么创建和使用单例非常简单。

  1. 创建实例
  void main() {
      runApp(
        MultiProvider(
          providers: [
            ChangeNotifierProvider(create: (context) => SomeModel()),
            Provider(create: (context) => SomeClassToBeUsedAsSingleton()),
          ],
          child: MyApp(),
        ),
      );
    }
Run Code Online (Sandbox Code Playgroud)
  1. 获取实例
Widget build(BuildContext context) { 
  var instance = Provider.of<SomeClassToBeUsedAsSingleton>(context); 
  ...
Run Code Online (Sandbox Code Playgroud)


dav*_*ode 7

在阅读了所有替代方案后,我想出了这个,这让我想起了“经典的单身人士”:

class AccountService {
  static final _instance = AccountService._internal();

  AccountService._internal();

  static AccountService getInstance() {
    return _instance;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 我会更改“instance”属性中的“getInstance”方法,如下所示:“static AccountService get instance =&gt; _instance;” (3认同)

Ivá*_*oed 6

在这个例子中,我做了其他一些在想要使用 Singleton 时也是必要的事情。例如:

  • 将值传递给单例的构造函数
  • 在构造函数内部初始化一个值
  • 为单例变量设置一个值
  • 能够访问和访问这些值

像这样:

class MySingleton {

  static final MySingleton _singleton = MySingleton._internal();

  String _valueToBeSet;
  String _valueAlreadyInSingleton;
  String _passedValueInContructor;

  get getValueToBeSet => _valueToBeSet;

  get getValueAlreadyInSingleton => _valueAlreadyInSingleton;

  get getPassedValueInConstructor => _passedValueInContructor;

  void setValue(newValue) {
    _valueToBeSet = newValue;
  }

  factory MySingleton(String passedString) {
    _singleton._valueAlreadyInSingleton = "foo";
    _singleton._passedValueInContructor = passedString;

    return _singleton;
  }

  MySingleton._internal();
}
Run Code Online (Sandbox Code Playgroud)

MySingleton 的使用:

void main() {

MySingleton mySingleton =  MySingleton("passedString");
mySingleton.setValue("setValue");
print(mySingleton.getPassedValueInConstructor);
print(mySingleton.getValueToBeSet);
print(mySingleton.getValueAlreadyInSingleton);

}
Run Code Online (Sandbox Code Playgroud)


jit*_*555 6

Singleton可以使用 null 安全运算符和工厂构造函数更好地创建对象。

class Singleton {
  static Singleton? _instance;

  Singleton._internal();

  factory Singleton() => _instance ??= Singleton._internal();
  
  void someMethod() {
    print("someMethod Called");
  }
}
Run Code Online (Sandbox Code Playgroud)

用法:

void main() {
  Singleton object = Singleton();
  object.someMethod(); /// Output: someMethod Called
}
Run Code Online (Sandbox Code Playgroud)

注意: ??是一个 Null 感知运算符,如果左侧值为 null,则返回右侧值,这意味着在我们的示例中_instance ?? Singleton._internal();Singleton._internal()将在对象被调用时第一次返回,其余的_instance将返回。


Daz*_*ong 5

修改了@Seth Ladd 回答谁更喜欢 Swift 风格的单身人士,例如.shared

class Auth {
  // singleton
  static final Auth _singleton = Auth._internal();
  factory Auth() => _singleton;
  Auth._internal();
  static Auth get shared => _singleton;

  // variables
  String username;
  String password;
}
Run Code Online (Sandbox Code Playgroud)

样本:

Auth.shared.username = 'abc';
Run Code Online (Sandbox Code Playgroud)


小智 5

Dart 2.13版本开始,使用late关键字很容易。Late关键字允许我们延迟实例化对象。

例如,您可以看到它:

class LazySingletonExample {
  LazySingletonExample._() {
    print('instance created.');
  }

  static late final LazySingletonExample instance = LazySingletonExample._();
  
  
}
Run Code Online (Sandbox Code Playgroud)

注意:请记住,当您调用惰性instance字段时,它只会被实例化一次。

  • 在 Dart 中,全局变量和静态类变量默认是惰性的,因此在静态变量中添加“late”是多余的。只有将局部变量声明为“late”时,我们才能将其初始化推迟到实际使用时。 (2认同)

小智 5

** Dart Sound 空安全中的 Sigleton 范式**

此代码片段展示了如何在 dart 中实现单例 这通常用于我们每次都必须使用类的相同对象的情况,例如。在数据库事务中。

class MySingleton {
  static MySingleton? _instance;
  MySingleton._internal();
  factory MySingleton() {
    if (_instance == null) {
      _instance = MySingleton._internal();
    }
     return _instance!;
  }
}
Run Code Online (Sandbox Code Playgroud)


Jav*_*haq 5

如何在 dart flutter 中创建类的单例实例

  class ContactBook {
      ContactBook._sharedInstance();
      static final ContactBook _shared = ContactBook._sharedInstance();
      factory ContactBook() => _shared;
    }
Run Code Online (Sandbox Code Playgroud)