单例模式确保只创建一个类的一个实例。我如何在 Dart 中构建它?
23 回答
感谢 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
}
下面是在 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;
笔记:
我最初问这个问题,但发现上述所有方法都是有效的,选择很大程度上取决于个人喜好。
我觉得它不是很直观的阅读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 在他的游戏编程模式书中对单例的看法。
这是一个简单的答案:
首先,我们需要一个类类型的private
andstatic
属性。
其次,构造函数应该是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();
像这样在库中使用全局变量怎么样?
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 中走很长的路。
这是另一种可能的方式:
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;
}
const 构造函数和工厂的 Dart 单例
class Singleton {
factory Singleton() =>
Singleton._internal_();
Singleton._internal_();
}
void main() {
print(new Singleton() == new Singleton());
print(identical(new Singleton() , new 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);
}
实例化后无法更改对象的单例
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}
自版本以来,使用关键字Dart 2.13
非常容易。关键字允许我们懒惰地实例化对象。late
Late
例如,您可以看到它:
class LazySingletonExample {
LazySingletonExample._() {
print('instance created.');
}
static late final LazySingletonExample instance = LazySingletonExample._();
}
注意:请记住,它只会在您调用惰性
instance
字段时实例化一次。
在阅读了所有替代方案后,我想到了这个,这让我想起了一个“经典单身人士”:
class AccountService {
static final _instance = AccountService._internal();
AccountService._internal();
static AccountService getInstance() {
return _instance;
}
}
这就是我在项目中实现单例的方式
灵感来自 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);
修改了@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';
这是一个结合其他解决方案的简洁示例。可以通过以下方式访问单例:
- 使用
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
}
如果您碰巧使用 Flutter 和provider
package 进行状态管理,那么创建和使用单例非常简单。
- 创建实例
void main() { runApp( MultiProvider( providers: [ ChangeNotifierProvider(create: (context) => SomeModel()), Provider(create: (context) => SomeClassToBeUsedAsSingleton()), ], child: MyApp(), ), ); }
- 获取实例
Widget build(BuildContext context) { var instance = Provider.of<SomeClassToBeUsedAsSingleton>(context); ...
由于我不太喜欢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));
这应该有效。
class GlobalStore {
static GlobalStore _instance;
static GlobalStore get instance {
if(_instance == null)
_instance = new GlobalStore()._();
return _instance;
}
_(){
}
factory GlobalStore()=> instance;
}
** Dart Sound Null Safety 中的 Sigleton 范式**
此代码片段展示了如何在 dart 中实现单例 这通常用于我们每次都必须使用类的相同对象的情况,例如。在数据库事务中。
class MySingleton {
static MySingleton? _instance;
MySingleton._internal();
factory MySingleton() {
if (_instance == null) {
_instance = MySingleton._internal();
}
return _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;
}
}
我在 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
使用 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
将返回。
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.