El patrón singleton garantiza que solo se cree una instancia de una clase. ¿Cómo construyo esto en Dart?
El patrón singleton garantiza que solo se cree una instancia de una clase. ¿Cómo construyo esto en Dart?
Respuestas:
Gracias a los constructores de fábrica de Dart , es fácil construir un singleton:
class Singleton {
static final Singleton _singleton = Singleton._internal();
factory Singleton() {
return _singleton;
}
Singleton._internal();
}
Puedes construirlo así
main() {
var s1 = Singleton();
var s2 = Singleton();
print(identical(s1, s2)); // true
print(s1 == s2); // true
}
new
no significa "construir uno nuevo" aquí, solo dice "ejecutar el constructor".
new
palabra clave sugiere que la clase se instancia, lo que no es. Iría por un método estático get()
o getInstance()
como lo hago en Java.
Singleton._internal();
que parece una llamada a un método cuando realmente es una definición de constructor. Ahí está el _internal
nombre. Y existe el ingenioso punto de diseño del lenguaje que Dart le permite comenzar (¿lanzarse?) Usando un constructor ordinario y luego, si es necesario, cambiarlo a un factory
método sin cambiar todas las llamadas.
Aquí hay una comparación de varias formas diferentes de crear un singleton en 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();
}
Los singletons anteriores se instancian de esta manera:
SingletonOne one = SingletonOne();
SingletonTwo two = SingletonTwo.instance;
SingletonThree three = SingletonThree.instance;
Nota:
Originalmente hice esto como una pregunta , pero descubrí que todos los métodos anteriores son válidos y la elección depende en gran medida de la preferencia personal.
static final SingletonThree instance = SingletonThree()
. Lo mismo ocurre con la segunda forma para _instance
. No sé cuál es la desventaja de no usar un constructor privado. Hasta ahora, no encuentro ningún problema en mi camino. La segunda y tercera forma no están bloqueando la llamada al constructor predeterminado de todos modos.
SingletonThree instance2 = SingletonThree()
. Si intenta hacer esto cuando hay un constructor privado, obtendrá el error:The class 'SingletonThree' doesn't have a default constructor.
No me parece una lectura muy intuitiva new Singleton()
. Debe leer los documentos para saber que en new
realidad no está creando una nueva instancia, como lo haría normalmente.
Aquí hay otra forma de hacer singletons (Básicamente lo que Andrew dijo anteriormente).
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();
}
Tenga en cuenta que el singleton no se crea hasta la primera vez que se llama al getter debido a la inicialización diferida de Dart.
Si lo prefiere, también puede implementar singletons como getter estático en la clase singleton. es decir Thing.singleton
, en lugar de un captador de nivel superior.
Lea también la versión de Bob Nystrom sobre los singletons de su libro de patrones de programación de juegos .
¿Qué tal si usas una variable global dentro de tu biblioteca, así?
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 está mal visto?
El patrón singleton es necesario en Java, donde el concepto de globals no existe, pero parece que no deberías tener que recorrer el camino en Dart.
Singleton
acceso a la instancia . En mi ejemplo anterior, la Singleton
clase es un singleton real, solo una instancia de Singleton
puede existir en el aislamiento.
new Singleton._internal()
tantas veces como quiera, creando muchos objetos de la Singleton
clase. Si la Impl
clase en el ejemplo de Andrew fuera privada ( _Impl
), sería la misma que su ejemplo. Por otro lado, singleton es un antipatrón y nadie debería usarlo de todos modos.
Singelton._internal()
. Puede argumentar que los desarrolladores de la clase singelton también podrían instaurar la clase varias veces. Claro que existe el enum singelton, pero para mí es solo de uso teórico. Una enumeración es una enumeración, no un singelton ... En cuanto al uso de variables de nivel superior (@Andrew y @Seth): ¿Nadie podría escribir en la variable de nivel superior? De ninguna manera está protegido, ¿o me estoy perdiendo algo?
Aquí hay otra forma posible:
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 de 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 que no puede cambiar el objeto después de la instancia
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}
Respuesta de @Seth Ladd modificada para quién prefiere el estilo Swift de singleton como .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;
}
Muestra:
Auth.shared.username = 'abc';
Después de leer todas las alternativas se me ocurrió esto, que me recuerda un "singleton clásico":
class AccountService {
static final _instance = AccountService._internal();
AccountService._internal();
static AccountService getInstance() {
return _instance;
}
}
getInstance
método en una instance
propiedad como esta:static AccountService get instance => _instance;
Aquí hay un ejemplo conciso que combina las otras soluciones. El acceso al singleton se puede hacer mediante:
singleton
variable global que apunta a la instancia.Singleton.instance
patrón común .Nota: Debe implementar solo una de las tres opciones para que el código que use el singleton sea coherente.
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 necesita hacer una inicialización compleja, solo tendrá que hacerlo antes de usar la instancia más adelante en el programa.
Ejemplo
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
}
Hola, ¿qué tal algo como esto? Implementación muy simple, el inyector en sí mismo es singleton y también agregó clases en él. Por supuesto, se puede extender muy fácilmente. Si está buscando algo más sofisticado, consulte este paquete: 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;
}
}
Esto debería funcionar.
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._();
haría. ¿Qué se _(){}
supone que debe hacer? Esto parece redundante.
Como no soy muy aficionado a usar la new
palabra clave u otro constructor como llamadas en singletons, preferiría usar un getter estático llamado, inst
por ejemplo:
// 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");
}
ejemplo de uso:
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));