如何在Dart中构建单例?

单例模式确保只创建一个类的实例。我如何在达特建立这个?

231457 次浏览

多亏了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
}

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

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中是必要的,因为在Java中不存在全局变量的概念,但在Dart中似乎不需要绕这么长的路。

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

这里有另一种方法来做单例(基本上是Andrew上面说的)。

lib / thing.dart

library thing;


final Thing thing = new Thing._private();


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

main.dart

import 'package:thing/thing.dart';


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

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

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

也可以阅读Bob Nystrom对从他的游戏编程模式书的看法。

由const构造函数&工厂

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

 

void main() {
print(new Singleton() == new Singleton());
print(identical(new Singleton() , new Singleton()));
}

这是另一种可能的方式:

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

下面是一个组合了其他解决方案的简洁示例。可以通过以下方式访问单例对象:

  • 使用指向实例的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
}

这应该有用。

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


_(){


}
factory GlobalStore()=> instance;




}

由于我不太喜欢使用new关键字或其他构造函数,如对单例对象的调用,我更喜欢使用名为inst的静态getter,例如:

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

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

下面是在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;

注意:

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

你好,这样的东西怎么样?非常简单的实现,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;
}
}

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

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}

在阅读了所有的选项后,我想到了这个,这让我想起了一个“经典的单例”:

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


AccountService._internal();


static AccountService getInstance() {
return _instance;
}
}

这里有一个简单的答案:

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

  • 其次,构造函数应该是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();

这就是我如何在我的项目中实现单例

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

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

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


void doSomething() {
}
}


Singleton.shared.doSomething();

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

class Singleton{
Singleton._();
static final Singleton db = Singleton._();
}

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

  • 将一个值传递给单例对象的构造函数
  • 在构造函数内部初始化一个值
  • 为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);


}

如果你碰巧使用Flutter和provider包进行状态管理,创建和使用单例是相当简单的。

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

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

作为一个例子,你可以看到:

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


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

  

}

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

你可以只使用Constant构造函数。

class Singleton {
const Singleton(); //Constant constructor
  

void hello() { print('Hello world'); }
}

例子:

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

根据文件:

恒定的构造函数

如果类生成永不更改的对象,则可以使这些对象成为编译时常量。为此,定义一个const构造函数,并确保所有实例变量都是final变量。

** Sigleton范式在Dart声音零安全**

这个代码片段展示了如何在dart中实现单例 这通常用于每次必须使用同一个类的相同对象的情况。

class MySingleton {
static MySingleton? _instance;
MySingleton._internal();
factory MySingleton() {
if (_instance == null) {
_instance = MySingleton._internal();
}
return _instance!;
}
}

Singleton对象可以更好地使用空安全操作符和工厂构造函数创建。

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将返回。

这是我做单例的方式,接受参数(你可以直接粘贴到https://dartpad.dev/):

void main() {
  

Logger x = Logger('asd');
Logger y = Logger('xyz');
  

x.display('Hello');
y.display('Hello There');
}




class Logger{
  

  

Logger._(this.message);
final String message;
static Logger _instance = Logger._('??!?*');
factory Logger(String message){
if(_instance.message=='??!?*'){
_instance = Logger._(message);
}
return _instance;
}
  

void display(String prefix){
print(prefix+' '+message);
}
  

}

输入:

Hello asd
Hello There asd

“? ? ! ?*'你看到的只是一个工作区,我做了初始化_instance变量暂时没有使它成为一个Logger?类型(空安全)。

如何在飞镖扑动中创建类的单例实例

  class ContactBook {
ContactBook._sharedInstance();
static final ContactBook _shared = ContactBook._sharedInstance();
factory ContactBook() => _shared;
}

创建单例

class PermissionSettingService {
static PermissionSettingService _singleton = PermissionSettingService._internal();


factory PermissionSettingService() {
return _singleton;
}


PermissionSettingService._internal();
}

重置单例

// add this function inside the function
void reset() {
_singleton = PermissionSettingService._internal();
}

在Dart中创建Singleton没有什么棘手的。您可以在顶级(全局)位置声明任何变量,默认情况下这是一个单例。也可以将变量声明为类的静态成员。这是一个单例A

class A {}


final a = A();

但是,上面的内容不允许您替换测试的实例。另一个问题是,随着应用程序的复杂性增长,你可能想要将全局变量或静态变量转换为类中的瞬态依赖项。如果你使用依赖注入,你可以在任何时候改变你的组合中的依赖。这是一个使用ioc_container在应用程序根中配置A的单例实例的例子。你可以在任何时候通过使用add而不是addSingletonService将其更改为瞬态依赖项

import 'package:ioc_container/ioc_container.dart';


class A {}


void main(List<String> arguments) {
final builder = IocContainerBuilder()..addSingletonService(A());
final container = builder.toContainer();
final a1 = container<A>();
final a2 = container<A>();
print(identical(a1, a2));
}

上面打印true,因为应用程序只会生成一个A实例。