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
}
new
não significa "construir um novo" aqui, apenas diz "executar o construtor".
new
palavra-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 _internal
nome. 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 factory
mé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.
Singleton
acesso à instância . No meu exemplo acima, a Singleton
classe é um singleton real, apenas uma instância de Singleton
pode existir no isolado.
new Singleton._internal()
quantas vezes quiser, criando muitos objetos da Singleton
classe. Se a Impl
classe 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;
}
}
getInstance
método em uma instance
propriedade 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 new
palavra - chave ou outro construtor como chamadas em singletons, eu preferiria usar um getter estático chamado, inst
por 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));