Le modèle singleton garantit qu'une seule instance d'une classe est créée. Comment créer cela dans Dart?
Le modèle singleton garantit qu'une seule instance d'une classe est créée. Comment créer cela dans Dart?
Réponses:
Grâce aux constructeurs d'usine de Dart , il est facile de créer un singleton:
class Singleton {
static final Singleton _singleton = Singleton._internal();
factory Singleton() {
return _singleton;
}
Singleton._internal();
}
Vous pouvez le construire comme ça
main() {
var s1 = Singleton();
var s2 = Singleton();
print(identical(s1, s2)); // true
print(s1 == s2); // true
}
new
ça ne veut pas dire "en construire un nouveau" ici, ça dit juste "exécuter le constructeur".
new
mot clé suggère que la classe est instanciée, ce qui n'est pas le cas. J'irais pour une méthode statique get()
ou getInstance()
comme je le fais en Java.
Singleton._internal();
qui ressemble à un appel de méthode alors qu'il s'agit vraiment d'une définition de constructeur. Voilà le _internal
nom. Et il y a le point de conception du langage astucieux que Dart vous permet de commencer (darder?) En utilisant un constructeur ordinaire, puis, si nécessaire, de le changer en factory
méthode sans changer tous les appelants.
Voici une comparaison de plusieurs façons différentes de créer un singleton dans Dart.
class SingletonOne {
SingletonOne._privateConstructor();
static final SingletonOne _instance = SingletonOne._privateConstructor();
factory SingletonOne() {
return _instance;
}
}
class SingletonTwo {
SingletonTwo._privateConstructor();
static final SingletonTwo _instance = SingletonTwo._privateConstructor();
static SingletonTwo get instance => _instance;
}
class SingletonThree {
SingletonThree._privateConstructor();
static final SingletonThree instance = SingletonThree._privateConstructor();
}
Les singletons ci-dessus sont instanciés comme ceci:
SingletonOne one = SingletonOne();
SingletonTwo two = SingletonTwo.instance;
SingletonThree three = SingletonThree.instance;
Remarque:
J'ai initialement posé cette question , mais j'ai découvert que toutes les méthodes ci-dessus sont valides et que le choix dépend en grande partie de vos préférences personnelles.
static final SingletonThree instance = SingletonThree()
. Il en va de même pour la deuxième méthode _instance
. Je ne sais pas quel est l'inconvénient de ne pas utiliser de constructeur privé. Jusqu'à présent, je ne trouve aucun problème sur mon chemin. De toute façon, les deuxième et troisième moyens ne bloquent pas l'appel au constructeur par défaut.
SingletonThree instance2 = SingletonThree()
. Si vous essayez de faire cela quand il y a un constructeur privé, vous obtiendrez l'erreur:The class 'SingletonThree' doesn't have a default constructor.
Je ne trouve pas la lecture très intuitive new Singleton()
. Vous devez lire la documentation pour savoir que cela new
ne crée pas réellement une nouvelle instance, comme cela le ferait normalement.
Voici une autre façon de faire des singletons (en gros ce qu'Andrew a dit ci-dessus).
lib / chose.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();
}
Notez que le singleton n'est créé que lors du premier appel du getter en raison de l'initialisation paresseuse de Dart.
Si vous préférez, vous pouvez également implémenter des singletons comme getter statique sur la classe singleton. c'est-à Thing.singleton
- dire au lieu d'un getter de premier niveau.
Lisez également le point de vue de Bob Nystrom sur les singletons dans son livre de modèles de programmation de jeu .
Qu'en est-il simplement d'utiliser une variable globale dans votre bibliothèque, comme ça?
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);
}
Ou est-ce mal vu?
Le modèle singleton est nécessaire en Java où le concept de globals n'existe pas, mais il semble que vous ne devriez pas avoir besoin de parcourir le long chemin dans Dart.
Singleton
facile d'accès. Dans mon exemple ci-dessus, la Singleton
classe est un vrai singleton, une seule instance de Singleton
peut exister dans l'isolat.
new Singleton._internal()
autant de fois qu'elle le souhaite, créant de nombreux objets de la Singleton
classe. Si la Impl
classe de l'exemple d'Andrew était private ( _Impl
), ce serait la même chose que votre exemple. D'un autre côté, singleton est un anti-modèle et personne ne devrait l'utiliser de toute façon.
Singelton._internal()
. Vous pouvez affirmer que les développeurs de la classe singelton pourraient également installer la classe plusieurs fois. Bien sûr, il y a l'énum singelton, mais pour moi, ce n'est que théorique. Une énumération est une énumération, pas un singelton ... Quant à l'utilisation des variables de niveau supérieur (@Andrew et @Seth): Personne ne pourrait écrire dans la variable de niveau supérieur? Il n'est en aucun cas protégé ou est-ce que je manque quelque chose?
Voici un autre moyen possible:
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;
}
Dart singleton par constructeur et usine const
class Singleton {
factory Singleton() =>
const Singleton._internal_();
const Singleton._internal_();
}
void main() {
print(new Singleton() == new Singleton());
print(identical(new Singleton() , new Singleton()));
}
Singleton qui ne peut pas changer l'objet après l'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, idade: 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}
Réponse modifiée de @Seth Ladd pour qui préfère le style de singleton Swift comme .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;
}
Échantillon:
Auth.shared.username = 'abc';
Après avoir lu toutes les alternatives, je suis venu avec ceci, qui me rappelle un "singleton classique":
class AccountService {
static final _instance = AccountService._internal();
AccountService._internal();
static AccountService getInstance() {
return _instance;
}
}
getInstance
méthode dans une instance
propriété comme celle-ci:static AccountService get instance => _instance;
Voici un exemple concis qui combine les autres solutions. L'accès au singleton peut se faire par:
singleton
variable globale qui pointe vers l'instance.Singleton.instance
modèle commun .Remarque: vous ne devez implémenter qu'une des trois options afin que le code utilisant le singleton soit cohérent.
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;
}
Si vous devez effectuer une initialisation complexe, vous devrez simplement le faire avant d'utiliser l'instance ultérieurement dans le programme.
Exemple
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
}
Bonjour et quelque chose comme ça? Implémentation très simple, Injector lui-même est singleton et y a également ajouté des classes. Bien sûr, peut être étendu très facilement. Si vous cherchez quelque chose de plus sophistiqué, vérifiez ce package: 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;
}
}
Cela devrait fonctionner.
class GlobalStore {
static GlobalStore _instance;
static GlobalStore get instance {
if(_instance == null)
_instance = new GlobalStore()._();
return _instance;
}
_(){
}
factory GlobalStore()=> instance;
}
static GlobalStore get instance => _instance ??= new GlobalStore._();
ferait. Que _(){}
doit-on faire? Cela semble redondant.
Comme je n'aime pas beaucoup utiliser le new
mot - clé ou un autre constructeur comme les appels sur des singletons, je préférerais utiliser un getter statique appelé inst
par exemple:
// 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");
}
exemple d'utilisation:
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));