373

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

4

23 回答 23

549

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

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

  factory Singleton() {
    return _singleton;
  }

  Singleton._internal();
}

你可以像这样构造它

main() {
  var s1 = Singleton();
  var s2 = Singleton();
  print(identical(s1, s2));  // true
  print(s1 == s2);           // true
}
于 2012-09-29T02:59:51.820 回答
417

下面是在 Dart 中创建单例的几种不同方法的比较。

1.工厂构造函数

class SingletonOne {

  SingletonOne._privateConstructor();

  static final SingletonOne _instance = SingletonOne._privateConstructor();

  factory SingletonOne() {
    return _instance;
  }

}

2. 带getter的静态字段

class SingletonTwo {

  SingletonTwo._privateConstructor();

  static final SingletonTwo _instance = SingletonTwo._privateConstructor();

  static SingletonTwo get instance => _instance;
  
}

3.静态场

class SingletonThree {

  SingletonThree._privateConstructor();

  static final SingletonThree instance = SingletonThree._privateConstructor();
  
}

如何实例化

上面的单例实例化如下:

SingletonOne one = SingletonOne();
SingletonTwo two = SingletonTwo.instance;
SingletonThree three = SingletonThree.instance;

笔记:

我最初问这个问题,但发现上述所有方法都是有效的,选择很大程度上取决于个人喜好。

于 2019-03-26T00:25:07.260 回答
49

我觉得它不是很直观的阅读new Singleton()。您必须阅读文档才能知道这new实际上并没有像通常那样创建新实例。

这是另一种做单例的方法(基本上是安德鲁上面所说的)。

lib/thing.dart

library thing;

final Thing thing = new Thing._private();

class Thing {
   Thing._private() { print('#2'); }
   foo() {
     print('#3');
   }
}

主要.dart

import 'package:thing/thing.dart';

main() {
  print('#1');
  thing.foo();
}

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

如果您愿意,您还可以在单​​例类上将单例实现为静态 getter。即Thing.singleton,而不是顶级吸气剂。

另请阅读 Bob Nystrom 在他的游戏编程模式书中对单例的看法。

于 2013-12-12T23:00:49.867 回答
42

这是一个简单的答案:

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

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

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

延迟加载的实现

class Singleton {
  static Singleton? _instance;

  Singleton._();

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

  void someMethod(){
    ...
  }

  ...
}

使用急切加载实现

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

  Singleton._();

  static Singleton get instance => _instance;

  void someMethod(){
    ...
  }

  ...
}

用法

Singleton.instance.someMethod();
于 2019-11-25T06:22:15.063 回答
25

像这样在库中使用全局变量怎么样?

single.dart

library singleton;

var Singleton = new Impl();

class Impl {
  int i;
}

main.dart

import 'single.dart';

void main() {
  var a = Singleton;
  var b = Singleton;
  a.i = 2;
  print(b.i);
}

还是对此不屑一顾?

在不存在全局概念的 Java 中,单例模式是必需的,但似乎您不需要在 Dart 中走很长的路。

于 2012-09-29T03:41:48.970 回答
21

这是另一种可能的方式:

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;
}
于 2014-09-25T17:35:08.743 回答
14

const 构造函数和工厂的 Dart 单例

class Singleton {
  factory Singleton() =>
    Singleton._internal_();
  Singleton._internal_();
}
 
 
void main() {
  print(new Singleton() == new Singleton());
  print(identical(new Singleton() , new Singleton()));
}
于 2013-12-20T13:00:30.387 回答
12

在这个例子中,我做了一些在想要使用 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();
}

MySingleton 的用法:

void main() {

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

}
于 2021-02-26T20:33:54.457 回答
12

实例化后无法更改对象的单例

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}
于 2019-06-22T20:10:37.333 回答
8

自版本以来,使用关键字Dart 2.13非常容易。关键字允许我们懒惰地实例化对象。lateLate

例如,您可以看到它:

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

  static late final LazySingletonExample instance = LazySingletonExample._();
  
  
}

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

于 2021-05-22T19:53:57.540 回答
8

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

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

  AccountService._internal();

  static AccountService getInstance() {
    return _instance;
  }
}
于 2019-10-24T09:33:57.447 回答
7

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

灵感来自 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();
}

我的项目中的示例

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);
于 2020-07-28T07:29:23.633 回答
7

修改了@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;
}

样本:

Auth.shared.username = 'abc';
于 2019-01-13T08:18:45.697 回答
7

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

  • 使用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;
}

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

例子

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
}
于 2018-06-25T20:45:20.870 回答
6

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

  1. 创建实例
  void main() {
      runApp(
        MultiProvider(
          providers: [
            ChangeNotifierProvider(create: (context) => SomeModel()),
            Provider(create: (context) => SomeClassToBeUsedAsSingleton()),
          ],
          child: MyApp(),
        ),
      );
    }
  1. 获取实例
Widget build(BuildContext context) { 
  var instance = Provider.of<SomeClassToBeUsedAsSingleton>(context); 
  ...
于 2021-02-28T21:35:52.663 回答
3

由于我不太喜欢new在单例上使用关键字或其他构造函数(例如调用),因此我更喜欢使用静态 getter inst,例如:

// the singleton class
class Dao {
    // singleton boilerplate
        Dao._internal() {}
        static final Dao _singleton = new Dao._internal();
        static get inst => _singleton;

    // business logic
        void greet() => print("Hello from singleton");
}

示例用法:

Dao.inst.greet();       // call a method

// Dao x = new Dao();   // compiler error: Method not found: 'Dao'

// verify that there only exists one and only one instance
assert(identical(Dao.inst, Dao.inst));
于 2018-12-13T16:33:25.140 回答
3

这应该有效。

class GlobalStore {
    static GlobalStore _instance;
    static GlobalStore get instance {
       if(_instance == null)
           _instance = new GlobalStore()._();
       return _instance;
    }

    _(){

    }
    factory GlobalStore()=> instance;


}
于 2018-10-13T08:31:58.680 回答
3

** Dart Sound Null Safety 中的 Sigleton 范式**

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

class MySingleton {
  static MySingleton? _instance;
  MySingleton._internal();
  factory MySingleton() {
    if (_instance == null) {
      _instance = MySingleton._internal();
    }
     return _instance!;
  }
}
于 2021-07-28T14:40:37.207 回答
3

你好,这样的事情怎么样?非常简单的实现,Injector 本身是单例的,并且还在其中添加了类。当然可以很容易地扩展。如果您正在寻找更复杂的东西,请检查这个包:https ://pub.dartlang.org/packages/flutter_simple_dependency_injection

void main() {  
  Injector injector = Injector();
  injector.add(() => Person('Filip'));
  injector.add(() => City('New York'));

  Person person =  injector.get<Person>(); 
  City city =  injector.get<City>();

  print(person.name);
  print(city.name);
}

class Person {
  String name;

  Person(this.name);
}

class City {
  String name;

  City(this.name);
}


typedef T CreateInstanceFn<T>();

class Injector {
  static final Injector _singleton =  Injector._internal();
  final _factories = Map<String, dynamic>();

  factory Injector() {
    return _singleton;
  }

  Injector._internal();

  String _generateKey<T>(T type) {
    return '${type.toString()}_instance';
  }

  void add<T>(CreateInstanceFn<T> createInstance) {
    final typeKey = _generateKey(T);
    _factories[typeKey] = createInstance();
  }

  T get<T>() {
    final typeKey = _generateKey(T);
    T instance = _factories[typeKey];
    if (instance == null) {
      print('Cannot find instance for type $typeKey');
    }

    return instance;
  }
}
于 2019-05-09T21:56:41.647 回答
1

我在 dart 和之前的 Swift 上使用过这个简单的模式。我喜欢它简洁且只有一种使用方式。

class Singleton {
  static Singleton shared = Singleton._init();
  Singleton._init() {
    // init work here
  }

  void doSomething() {
  }
}

Singleton.shared.doSomething();
于 2021-01-23T03:35:23.080 回答
1

这也是一种创建 Singleton 类的方法

class Singleton{
  Singleton._();
  static final Singleton db = Singleton._();
}
于 2021-02-02T19:38:28.020 回答
1

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

class Singleton {
  static Singleton? _instance;

  Singleton._internal();

  factory Singleton() => _instance ??= Singleton._internal();
  
  void someMethod() {
    print("someMethod Called");
  }
}

用法:

void main() {
  Singleton object = Singleton();
  object.someMethod(); /// Output: someMethod Called
}

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

于 2021-09-29T10:29:28.927 回答
-1

You can just use the Constant constructors.

class Singleton {
  const Singleton(); //Constant constructor
  
  void hello() { print('Hello world'); }
}

Example:

Singleton s = const Singleton();
s.hello(); //Hello world

According with documentation:

Constant constructors

If your class produces objects that never change, you can make these objects compile-time constants. To do this, define a const constructor and make sure that all instance variables are final.

于 2021-07-13T13:45:13.090 回答