Tekli desen, bir sınıfın yalnızca bir örneğinin oluşturulmasını sağlar. Bunu Dart'da nasıl oluşturabilirim?
Tekli desen, bir sınıfın yalnızca bir örneğinin oluşturulmasını sağlar. Bunu Dart'da nasıl oluşturabilirim?
Yanıtlar:
Dart'ın fabrika inşaatçıları sayesinde , tek birton inşa etmek kolaydır:
class Singleton {
static final Singleton _singleton = Singleton._internal();
factory Singleton() {
return _singleton;
}
Singleton._internal();
}
Bu şekilde inşa edebilirsiniz
main() {
var s1 = Singleton();
var s2 = Singleton();
print(identical(s1, s2)); // true
print(s1 == s2); // true
}
new
burada "yeni bir tane inşa et" anlamına gelmiyor, sadece "kurucuyu çalıştır" diyor.
new
Anahtar kelime sınıfı ki değil, örneği göstermektedir. Statik bir yöntem get()
veya getInstance()
Java'da yaptığım gibi giderdim .
Singleton._internal();
Gerçekten bir yapıcı tanımı olduğunda bir yöntem çağrısına benzeyen garip sözdizimi var. Orada _internal
isim. Ve Dart'ın sıradan bir kurucu kullanarak başlamanıza (dart?) İzin vermesi ve ardından gerekirse factory
tüm arayanları değiştirmeden bir yönteme değiştirmesine izin veren şık bir dil tasarım noktası var .
Dart'da bir singleton oluşturmanın birkaç farklı yolunun karşılaştırması.
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();
}
Yukarıdaki singletonlar şu şekilde başlatılır:
SingletonOne one = SingletonOne();
SingletonTwo two = SingletonTwo.instance;
SingletonThree three = SingletonThree.instance;
Not:
Aslında bunu bir soru olarak sordum , ancak yukarıdaki tüm yöntemlerin geçerli olduğunu ve seçimin büyük ölçüde kişisel tercihlere bağlı olduğunu keşfettim.
static final SingletonThree instance = SingletonThree()
. Aynı şey ikinci yoldan gider _instance
. Özel bir kurucu kullanmamanın dezavantajını bilmiyorum. Şimdiye kadar, yolumda herhangi bir sorun bulamıyorum. İkinci ve üçüncü yollar, zaten varsayılan kurucuya olan çağrıyı engellemez.
SingletonThree instance2 = SingletonThree()
. Özel bir kurucu olduğunda bunu yapmaya çalışırsanız, hatayı alırsınız:The class 'SingletonThree' doesn't have a default constructor.
Çok sezgisel bir okuma bulamıyorum new Singleton()
. new
Normalde olduğu gibi yeni bir örnek oluşturmadığını bilmek için belgeleri okumalısınız .
İşte singleton yapmak için başka bir yol (temelde Andrew yukarıda söylediğini).
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();
}
Dart'ın tembel başlatması nedeniyle alıcı ilk kez çağrılıncaya kadar singletonun oluşturulmadığını unutmayın.
İsterseniz, singletonları singleton sınıfında statik alıcı olarak da uygulayabilirsiniz. yani Thing.singleton
, bir üst düzey alıcı yerine.
Ayrıca Bob Nystrom'un Oyun programlama kalıpları kitabından singletonları ele geçirmesini okuyun .
Kütüphanenizde sadece global bir değişken kullanmaya ne dersiniz?
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);
}
Yoksa kaşlarını çattı mı?
Tekton paterni, küresel kavramının olmadığı Java'da gereklidir, ancak Dart'da uzun süre dolaşmanıza gerek yok gibi görünüyor.
Singleton
erişimi kolaylaştırmak için üst düzey bir değişkeni doğru şekilde kullandı . Yukarıdaki Singleton
örneğimde, sınıf gerçek bir single'tır, Singleton
izolatta yalnızca bir örneği bulunabilir.
new Singleton._internal()
istediği kadar arayabilir ve Singleton
sınıfın birçok nesnesini oluşturabilir . Eğer Impl
Andrew örnekte sınıf (özel olarak _Impl
), bu örnekte olduğu gibi aynı olacaktır. Öte yandan, singleton bir antipatterndir ve kimse onu yine de kullanmamalıdır.
Singelton._internal()
? Singelton sınıfının geliştiricilerinin sınıfı birkaç kez de yaratabileceğini iddia edebilirsiniz. Tabii ki enum singelton var ama bana göre sadece teorik kullanım. Bir enum bir tekil değil, bir enum ... Üst düzey değişkenlerin kullanımına gelince (@Andrew ve @Seth): Üst düzey değişkene kimse yazılamadı mı? Hiçbir şekilde korunmuyor mu, yoksa bir şey mi kaçırıyorum?
İşte olası başka bir yol:
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;
}
Const yapıcı ve fabrika tarafından dart singleton
class Singleton {
factory Singleton() =>
const Singleton._internal_();
const Singleton._internal_();
}
void main() {
print(new Singleton() == new Singleton());
print(identical(new Singleton() , new Singleton()));
}
Örnekten sonra nesneyi değiştiremeyen singleton
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}
Swift tarzı singleton'u kimin tercih ettiği için @Seth Ladd cevabı değiştirildi .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;
}
Örneklem:
Auth.shared.username = 'abc';
Tüm alternatifleri okuduktan sonra bana bir "klasik singleton" hatırlatıyor:
class AccountService {
static final _instance = AccountService._internal();
AccountService._internal();
static AccountService getInstance() {
return _instance;
}
}
getInstance
bir instance
özellikte yöntemi değiştirmek istiyorum:static AccountService get instance => _instance;
İşte diğer çözümleri birleştiren kısa bir örnek. Singleton'a erişim şu şekilde yapılabilir:
singleton
genel bir değişken kullanma .Singleton.instance
örüntü.Not: singleton'u kullanan kodun tutarlı olması için üç seçenekten yalnızca birini uygulamanız gerekir.
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;
}
Karmaşık başlatma yapmanız gerekiyorsa, örneği programda daha sonra kullanmadan önce yapmanız gerekir.
Misal
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
}
Merhaba, böyle bir şeye ne dersin? Çok basit uygulama, Enjektör kendisi singleton ve ayrıca içine sınıflar ekledi. Tabii ki çok kolay genişletilebilir. Daha sofistike bir şey arıyorsanız bu paketi kontrol edin: 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;
}
}
Bu çalışmalı.
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._();
yapardım. Ne _(){}
yapmalı? Bu gereksiz görünüyor.
new
Anahtar kelime veya singleton çağrıları gibi başka bir kurucu kullanmak çok sevmiyorum gibi, inst
örneğin denilen statik bir alıcı kullanmayı tercih ederim :
// 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");
}
örnek kullanım:
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));