O padrão singleton garante que apenas uma instância de uma classe seja criada. Como faço para criar isso no Dart?
O padrão singleton garante que apenas uma instância de uma classe seja criada. Como faço para criar isso no Dart?
Respostas:
Graças aos construtores de fábrica da Dart , é fácil criar um singleton:
class Singleton {
static final Singleton _singleton = Singleton._internal();
factory Singleton() {
return _singleton;
}
Singleton._internal();
}
Você pode construir assim
main() {
var s1 = Singleton();
var s2 = Singleton();
print(identical(s1, s2)); // true
print(s1 == s2); // true
}
newnão significa "construir um novo" aqui, apenas diz "executar o construtor".
newpalavra-chave sugere que a classe é instanciada, o que não é. Eu iria para um método estático get()ou getInstance()como eu faço em Java.
Singleton._internal();que se parece com uma chamada de método quando é realmente uma definição de construtor. Aqui está o _internalnome. E há o ponto de design da linguagem bacana que o Dart permite que você inicie (dart out?) Usando um construtor comum e, se necessário, altere-o para um factorymétodo sem alterar todos os chamadores.
Aqui está uma comparação de várias maneiras diferentes de criar um singleton no 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();
}
Os singletons acima são instanciados assim:
SingletonOne one = SingletonOne();
SingletonTwo two = SingletonTwo.instance;
SingletonThree three = SingletonThree.instance;
Nota:
Eu originalmente fiz isso como uma pergunta , mas descobri que todos os métodos acima são válidos e a escolha depende em grande parte da preferência pessoal.
static final SingletonThree instance = SingletonThree(). O mesmo vale para o segundo caminho para _instance. Não sei qual é a desvantagem de não usar um construtor privado. Até agora, não encontro nenhum problema no meu caminho. A segunda e a terceira maneira não estão bloqueando a chamada para o construtor padrão de qualquer maneira.
SingletonThree instance2 = SingletonThree(). Se você tentar fazer isso quando houver um construtor privado, receberá o erro:The class 'SingletonThree' doesn't have a default constructor.
Não acho uma leitura muito intuitiva new Singleton(). Você precisa ler os documentos para saber quenew na verdade, não está criando uma nova instância, como faria normalmente.
Aqui está outra maneira de fazer singletons (basicamente o que Andrew disse acima).
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();
}
Observe que o singleton não é criado até a primeira vez que o getter é chamado devido à inicialização lenta do Dart.
Se preferir, também é possível implementar singletons como getter estático na classe singleton. ieThing.singleton , em vez de um getter de nível superior.
Leia também a opinião de Bob Nystrom sobre singletons de seu livro de padrões de programação de jogos .
Que tal usar apenas uma variável global em sua biblioteca?
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 isso é desaprovado?
O padrão singleton é necessário em Java, onde o conceito de globais não existe, mas parece que você não precisa percorrer o caminho mais longo no Dart.
Singletonacesso à instância . No meu exemplo acima, a Singletonclasse é um singleton real, apenas uma instância de Singletonpode existir no isolado.
new Singleton._internal()quantas vezes quiser, criando muitos objetos da Singletonclasse. Se a Implclasse no exemplo de Andrew fosse private ( _Impl), seria a mesma que o seu exemplo. Por outro lado, o singleton é um antipadrão e ninguém deve usá-lo de qualquer maneira.
Singelton._internal(). Você pode argumentar que os desenvolvedores da classe singelton também poderiam instatá-la várias vezes. Claro que existe o enum singelton, mas para mim é apenas de uso teórico. Um enum é um enum, não um singelton ... Quanto ao uso de variáveis de nível superior (@Andrew e @Seth): ninguém poderia escrever na variável de nível superior? É de nenhuma maneira protegida, ou estou faltando alguma coisa?
Aqui está outra maneira possível:
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 por 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 não pode alterar o objeto após a instância
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}
Resposta modificada de @Seth Ladd para quem prefere o 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;
}
Amostra:
Auth.shared.username = 'abc';
Depois de ler todas as alternativas, eu vim com isso, o que me lembra um "singleton clássico":
class AccountService {
static final _instance = AccountService._internal();
AccountService._internal();
static AccountService getInstance() {
return _instance;
}
}
getInstancemétodo em uma instancepropriedade como esta:static AccountService get instance => _instance;
Aqui está um exemplo conciso que combina as outras soluções. O acesso ao singleton pode ser feito por:
singleton variável global que aponta para a instância.Singleton.instance padrão .Nota: Você deve implementar apenas uma das três opções para que o código usando o singleton seja consistente.
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 você precisar fazer uma inicialização complexa, precisará fazê-lo antes de usar a instância posteriormente no programa.
Exemplo
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
}
Olá, que tal algo assim? Implementação muito simples, o próprio Injector é único e também adicionou classes a ele. Claro que pode ser estendido com muita facilidade. Se você está procurando algo mais sofisticado, verifique este pacote: 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;
}
}
Isso deve 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._();faria. O que _(){}deveria fazer? Isso parece redundante.
Como não gosto muito de usar a newpalavra - chave ou outro construtor como chamadas em singletons, eu preferiria usar um getter estático chamado, instpor exemplo:
// 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");
}
exemplo 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));