Il modello singleton garantisce che venga creata solo un'istanza di una classe. Come lo costruisco in Dart?
Il modello singleton garantisce che venga creata solo un'istanza di una classe. Come lo costruisco in Dart?
Risposte:
Grazie ai costruttori di fabbrica di Dart , è facile costruire un singleton:
class Singleton {
static final Singleton _singleton = Singleton._internal();
factory Singleton() {
return _singleton;
}
Singleton._internal();
}
Puoi costruirlo in questo modo
main() {
var s1 = Singleton();
var s2 = Singleton();
print(identical(s1, s2)); // true
print(s1 == s2); // true
}
new
non significa "costruirne uno nuovo" qui, dice solo "esegui il costruttore".
new
parola chiave suggerisce che la classe è istanziata, cosa che non lo è. Vorrei un metodo statico get()
o getInstance()
come faccio in Java.
Singleton._internal();
che assomiglia a una chiamata di metodo quando è davvero una definizione di costruttore. C'è il _internal
nome E c'è il nifty point design del linguaggio che Dart ti consente di iniziare (dart out?) Usando un normale costruttore e poi, se necessario, cambiarlo in un factory
metodo senza cambiare tutti i chiamanti.
Ecco un confronto tra diversi modi per creare un singleton 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();
}
I singoli sopra sono istanziati in questo modo:
SingletonOne one = SingletonOne();
SingletonTwo two = SingletonTwo.instance;
SingletonThree three = SingletonThree.instance;
Nota:
Inizialmente l'ho posto come una domanda , ma ho scoperto che tutti i metodi sopra indicati sono validi e la scelta dipende in gran parte dalle preferenze personali.
static final SingletonThree instance = SingletonThree()
. Lo stesso vale per la seconda strada _instance
. Non so quale sia lo svantaggio di non usare un costruttore privato. Finora non ho riscontrato alcun problema sulla mia strada. Il secondo e il terzo modo non stanno comunque bloccando la chiamata al costruttore predefinito.
SingletonThree instance2 = SingletonThree()
. Se provi a farlo quando c'è un costruttore privato, otterrai l'errore:The class 'SingletonThree' doesn't have a default constructor.
Non lo trovo una lettura molto intuitiva new Singleton()
. Devi leggere i documenti per sapere che in new
realtà non sta creando una nuova istanza, come farebbe normalmente.
Ecco un altro modo di fare singletons (in sostanza quello che ha detto Andrew sopra).
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();
}
Nota che il singleton non viene creato fino alla prima volta in cui viene chiamato il getter a causa dell'inizializzazione pigra di Dart.
Se preferisci, puoi anche implementare singleton come getter statico nella classe singleton. cioè Thing.singleton
, invece di un getter di livello superiore.
Leggi anche la versione di Bob Nystrom sui singoli dal suo libro sui modelli di programmazione del gioco .
Che ne dici di usare una variabile globale nella tua libreria, in questo modo?
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);
}
O questo è disapprovato?
Il modello singleton è necessario in Java dove non esiste il concetto di globali, ma sembra che non si debba fare molta strada in Dart.
Singleton
accesso all'istanza . Nel mio esempio sopra, la Singleton
classe è un vero singleton, solo un'istanza diSingleton
può mai esistere nell'isolato.
new Singleton._internal()
tutte le volte che vuole, creando molti oggetti della Singleton
classe. Se la Impl
classe nell'esempio di Andrew fosse privata ( _Impl
), sarebbe la stessa del tuo esempio. D'altra parte, singleton è un antipattern e nessuno dovrebbe usarlo comunque.
Singelton._internal()
. Si può sostenere che anche gli sviluppatori della classe singelton potrebbero istigare la classe più volte. Certo c'è l'enum singelton ma per me è solo di uso teorico. Un enum è un enum, non un singelton ... Per quanto riguarda l'uso delle variabili di livello superiore (@Andrew e @Seth): nessuno potrebbe scrivere sulla variabile di livello superiore? Non è affatto protetto o mi sto perdendo qualcosa?
Ecco un altro modo possibile:
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 di const constructor & factory
class Singleton {
factory Singleton() =>
const Singleton._internal_();
const Singleton._internal_();
}
void main() {
print(new Singleton() == new Singleton());
print(identical(new Singleton() , new Singleton()));
}
Singleton che non può cambiare l'oggetto dopo l'istanza
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}
Modificato @Seth Ladd risposta per chi preferisce lo stile Swift di singleton come .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;
}
Campione:
Auth.shared.username = 'abc';
Dopo aver letto tutte le alternative, ho pensato a questo, che mi ricorda un "singleton classico":
class AccountService {
static final _instance = AccountService._internal();
AccountService._internal();
static AccountService getInstance() {
return _instance;
}
}
getInstance
metodo in una instance
proprietà come questa:static AccountService get instance => _instance;
Ecco un esempio conciso che combina le altre soluzioni. L'accesso al singleton può essere effettuato tramite:
singleton
variabile globale che punta all'istanza.Singleton.instance
modello comune .Nota: è necessario implementare solo una delle tre opzioni in modo che il codice che utilizza il singleton sia coerente.
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;
}
Se è necessario eseguire un'inizializzazione complessa, è sufficiente farlo prima di utilizzare l'istanza in un secondo momento nel programma.
Esempio
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
}
Ciao, che ne dici di qualcosa del genere? Implementazione molto semplice, Injector stesso è singleton e ha anche aggiunto delle classi. Naturalmente può essere esteso molto facilmente. Se stai cercando qualcosa di più sofisticato controlla questo pacchetto: 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;
}
}
Questo dovrebbe funzionare.
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._();
farebbe. Cosa _(){}
dovrebbe fare? Questo sembra ridondante.
Dato che non mi piace molto usare la new
parola chiave o un altro costruttore come chiamate su singoli, preferirei usare un getter statico chiamato inst
ad esempio:
// 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");
}
esempio di utilizzo:
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));