Das Singleton-Muster stellt sicher, dass immer nur eine Instanz einer Klasse erstellt wird. Wie baue ich das in Dart?
Das Singleton-Muster stellt sicher, dass immer nur eine Instanz einer Klasse erstellt wird. Wie baue ich das in Dart?
Antworten:
Dank der Fabrikkonstrukteure von Dart ist es einfach, einen Singleton zu bauen:
class Singleton {
static final Singleton _singleton = Singleton._internal();
factory Singleton() {
return _singleton;
}
Singleton._internal();
}
Sie können es so konstruieren
main() {
var s1 = Singleton();
var s2 = Singleton();
print(identical(s1, s2)); // true
print(s1 == s2); // true
}
new
bedeutet hier nicht "einen neuen konstruieren", sondern nur "den Konstruktor ausführen".
new
Schlüsselwort legt nahe, dass die Klasse instanziiert ist, was nicht der Fall ist. Ich würde mich für eine statische Methode entscheiden get()
oder getInstance()
wie in Java.
Singleton._internal();
, die wie ein Methodenaufruf aussieht, wenn es sich wirklich um eine Konstruktordefinition handelt. Da ist der _internal
Name. Und es gibt den raffinierten Sprachentwurfspunkt, mit dem Sie mit Dart mit einem normalen Konstruktor beginnen (Dart out?) Und ihn dann bei Bedarf in eine factory
Methode ändern können, ohne alle Aufrufer zu ändern.
Hier finden Sie einen Vergleich verschiedener Möglichkeiten zum Erstellen eines Singletons in 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();
}
Die obigen Singletons werden folgendermaßen instanziiert:
SingletonOne one = SingletonOne();
SingletonTwo two = SingletonTwo.instance;
SingletonThree three = SingletonThree.instance;
Hinweis:
Ich habe dies ursprünglich als Frage gestellt , aber festgestellt, dass alle oben genannten Methoden gültig sind und die Auswahl weitgehend von den persönlichen Vorlieben abhängt.
static final SingletonThree instance = SingletonThree()
. Gleiches gilt für den zweiten Weg _instance
. Ich weiß nicht, was der Nachteil ist, keinen privaten Konstruktor zu verwenden. Bisher finde ich keine Probleme im Weg. Die zweite und dritte Möglichkeit blockieren den Aufruf des Standardkonstruktors ohnehin nicht.
SingletonThree instance2 = SingletonThree()
. Wenn Sie versuchen, dies zu tun, wenn es einen privaten Konstruktor gibt, erhalten Sie die Fehlermeldung:The class 'SingletonThree' doesn't have a default constructor.
Ich finde es nicht sehr intuitiv zu lesen new Singleton()
. Sie müssen die Dokumente lesen, um das zu wissennew
keine neue Instanz erstellt wird, wie dies normalerweise der Fall ist.
Hier ist eine andere Möglichkeit, Singletons zu machen (im Grunde das, was Andrew oben gesagt hat).
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();
}
Beachten Sie, dass der Singleton erst erstellt wird, wenn der Getter aufgrund der verzögerten Initialisierung von Dart zum ersten Mal aufgerufen wird.
Wenn Sie möchten, können Sie Singletons auch als statischen Getter für die Singleton-Klasse implementieren. dhThing.singleton
anstelle eines Top-Level-Getters.
Lesen Sie auch Bob Nystroms Version von Singletons aus seinem Spielprogrammiermusterbuch .
Wie wäre es, wenn Sie einfach eine globale Variable in Ihrer Bibliothek verwenden?
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);
}
Oder ist das verpönt?
Das Singleton-Muster ist in Java erforderlich, wo das Konzept der Globalen nicht existiert, aber es scheint, dass Sie in Dart nicht den weiten Weg gehen müssen.
Singleton
Zugriff auf die Instanz zu vereinfachen. In meinem obigen Beispiel ist die Singleton
Klasse ein echter Singleton, von dem nur eine Instanz Singleton
im Isolat existieren kann.
new Singleton._internal()
so oft aufrufen, wie sie möchte, und so viele Objekte der Singleton
Klasse erstellen . Wenn die Impl
Klasse in Andrews Beispiel privat ( _Impl
) wäre, wäre sie dieselbe wie Ihr Beispiel. Auf der anderen Seite ist Singleton ein Antimuster und niemand sollte es trotzdem benutzen.
Singelton._internal()
. Sie können argumentieren, dass die Entwickler der Singelton-Klasse die Klasse auch mehrmals initiieren könnten. Sicher gibt es das Enum Singelton, aber für mich ist es nur von theoretischem Nutzen. Eine Aufzählung ist eine Aufzählung, kein Singelton ... Was die Verwendung von Variablen der obersten Ebene (@Andrew und @Seth) betrifft: Könnte niemand in die Variable der obersten Ebene schreiben? Es ist keineswegs geschützt, oder fehlt mir etwas?
Hier ist ein anderer möglicher Weg:
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 von Const Konstruktor & Fabrik
class Singleton {
factory Singleton() =>
const Singleton._internal_();
const Singleton._internal_();
}
void main() {
print(new Singleton() == new Singleton());
print(identical(new Singleton() , new Singleton()));
}
Singleton, der das Objekt nach der Instanz nicht ändern kann
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}
Modifizierte @ Seth Ladd-Antwort für diejenigen, die den Swift-Singleton-Stil bevorzugen, wie .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;
}
Stichprobe:
Auth.shared.username = 'abc';
Nachdem ich alle Alternativen gelesen hatte, kam ich auf diese Idee, die mich an einen "klassischen Singleton" erinnert:
class AccountService {
static final _instance = AccountService._internal();
AccountService._internal();
static AccountService getInstance() {
return _instance;
}
}
getInstance
Methode in einer instance
Eigenschaft wie dieser ändern :static AccountService get instance => _instance;
Hier ist ein kurzes Beispiel, das die anderen Lösungen kombiniert. Der Zugriff auf den Singleton kann erfolgen über:
singleton
globalen Variablen, die auf die Instanz verweist.Singleton.instance
Muster.Hinweis: Sie sollten nur eine der drei Optionen implementieren, damit der Code mit dem Singleton konsistent ist.
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;
}
Wenn Sie eine komplexe Initialisierung durchführen müssen, müssen Sie dies nur tun, bevor Sie die Instanz später im Programm verwenden.
Beispiel
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
}
Hallo was ist mit so etwas? Sehr einfache Implementierung, Injector selbst ist Singleton und hat auch Klassen hinzugefügt. Natürlich kann sehr einfach erweitert werden. Wenn Sie nach etwas Anspruchsvollerem suchen, überprüfen Sie dieses Paket: 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;
}
}
Das sollte funktionieren.
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._();
würdest du. Was _(){}
soll tun? Dies scheint überflüssig.
Da ich das new
Schlüsselwort oder einen anderen Konstruktor wie Aufrufe von Singletons nicht sehr gern verwende , würde ich lieber einen statischen Getter verwenden, der inst
zum Beispiel aufgerufen wird :
// 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");
}
Anwendungsbeispiel:
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));