Pola singleton memastikan hanya satu instance kelas yang pernah dibuat. Bagaimana cara membuat ini di Dart?
Pola singleton memastikan hanya satu instance kelas yang pernah dibuat. Bagaimana cara membuat ini di Dart?
Jawaban:
Berkat konstruktor pabrik Dart , mudah membangun singleton:
class Singleton {
static final Singleton _singleton = Singleton._internal();
factory Singleton() {
return _singleton;
}
Singleton._internal();
}
Anda dapat membangunnya seperti ini
main() {
var s1 = Singleton();
var s2 = Singleton();
print(identical(s1, s2)); // true
print(s1 == s2); // true
}
new
tidak berarti "membangun yang baru" di sini, itu hanya mengatakan "jalankan konstruktor".
new
kunci menunjukkan bahwa kelas adalah instantiated, yang bukan. Saya akan menggunakan metode statis get()
atau getInstance()
seperti yang saya lakukan di Jawa.
Singleton._internal();
yang terlihat seperti pemanggilan metode ketika itu benar-benar definisi konstruktor. Itu _internal
namanya. Dan ada titik desain bahasa yang bagus dimana Dart memungkinkan Anda memulai (dart out?) Menggunakan konstruktor biasa dan kemudian, jika perlu, ubah ke factory
metode tanpa mengubah semua penelepon.
Berikut ini adalah perbandingan beberapa cara berbeda untuk membuat singleton di 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();
}
Para lajang di atas dipakai seperti ini:
SingletonOne one = SingletonOne();
SingletonTwo two = SingletonTwo.instance;
SingletonThree three = SingletonThree.instance;
catatan:
Saya awalnya mengajukan ini sebagai pertanyaan , tetapi menemukan bahwa semua metode di atas valid dan pilihannya sangat tergantung pada preferensi pribadi.
static final SingletonThree instance = SingletonThree()
. Hal yang sama berlaku untuk cara kedua _instance
. Saya tidak tahu apa kerugiannya tidak menggunakan konstruktor pribadi. Sejauh ini, saya tidak menemukan masalah di jalan saya. Cara kedua dan ketiga tidak memblokir panggilan ke konstruktor default.
SingletonThree instance2 = SingletonThree()
. Jika Anda mencoba melakukan ini ketika ada konstruktor pribadi, Anda akan mendapatkan kesalahan:The class 'SingletonThree' doesn't have a default constructor.
Saya tidak menemukan itu bacaan yang sangat intuitif new Singleton()
. Anda harus membaca dokumen untuk mengetahui bahwa new
sebenarnya tidak membuat contoh baru, seperti biasanya.
Inilah cara lain untuk melakukan lajang (Pada dasarnya apa yang dikatakan Andrew di atas).
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();
}
Perhatikan bahwa singleton tidak dapat dibuat sampai pertama kali pengambil tersebut dipanggil karena inisialisasi malas dari Dart.
Jika Anda mau, Anda juga dapat menerapkan lajang sebagai pengambil statis di kelas lajang. yaitu Thing.singleton
, bukannya pengambil tingkat atas.
Baca juga pendapat Bob Nystrom tentang para lajang dari buku pola pemrograman Game-nya .
Bagaimana dengan hanya menggunakan variabel global dalam perpustakaan Anda, seperti itu?
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);
}
Atau ini disukai?
Pola tunggal diperlukan di Jawa di mana konsep global tidak ada, tetapi sepertinya Anda tidak perlu pergi jauh-jauh di Dart.
Singleton
mudah diakses. Dalam contoh saya di atas, Singleton
kelas adalah singleton nyata, hanya satu contoh yang Singleton
bisa ada di isolat.
new Singleton._internal()
sebanyak yang diinginkan, menciptakan banyak objek dari Singleton
kelas. Jika Impl
kelas dalam contoh Andrew adalah pribadi ( _Impl
), itu akan sama dengan contoh Anda. Di sisi lain, singleton adalah antipattern dan tidak ada yang harus menggunakannya.
Singelton._internal()
. Anda dapat berargumen bahwa pengembang dari kelas singelton dapat membuat instance kelas beberapa kali juga. Tentu ada enum singelton tetapi bagi saya itu hanya penggunaan teori. Enum adalah enum, bukan singelton ... Adapun penggunaan variabel tingkat atas (@Andrew dan @Seth): Tidak bisakah ada yang menulis ke variabel tingkat atas? Itu sama sekali tidak dilindungi, atau apakah saya kehilangan sesuatu?
Berikut cara lain yang mungkin:
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 oleh konstruktor & pabrik
class Singleton {
factory Singleton() =>
const Singleton._internal_();
const Singleton._internal_();
}
void main() {
print(new Singleton() == new Singleton());
print(identical(new Singleton() , new Singleton()));
}
Singleton yang tidak dapat mengubah objek setelah contoh
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}
Diubah @Seth Ladd untuk siapa yang lebih suka gaya Swift singleton seperti .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;
}
Sampel:
Auth.shared.username = 'abc';
Setelah membaca semua alternatif saya menemukan ini, yang mengingatkan saya pada "singleton klasik":
class AccountService {
static final _instance = AccountService._internal();
AccountService._internal();
static AccountService getInstance() {
return _instance;
}
}
getInstance
metode di instance
properti seperti ini:static AccountService get instance => _instance;
Berikut adalah contoh singkat yang menggabungkan solusi lain. Mengakses singleton dapat dilakukan dengan:
singleton
variabel global yang menunjuk ke instance.Singleton.instance
Pola umum .Catatan: Anda harus menerapkan hanya satu dari tiga opsi sehingga kode menggunakan singleton konsisten.
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;
}
Jika Anda perlu melakukan inisialisasi yang kompleks, Anda hanya perlu melakukannya sebelum menggunakan instance nanti dalam program.
Contoh
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
}
Halo bagaimana dengan yang seperti ini? Implementasi yang sangat sederhana, Injector sendiri adalah singleton dan juga menambahkan kelas ke dalamnya. Tentu saja dapat diperpanjang dengan sangat mudah. Jika Anda mencari sesuatu yang lebih canggih, periksa paket ini: 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;
}
}
Ini seharusnya bekerja.
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._();
akan lakukan. Apa yang _(){}
seharusnya dilakukan? Ini sepertinya berlebihan.
Karena saya tidak terlalu suka menggunakan new
kata kunci atau konstruktor lain seperti panggilan pada lajang, saya lebih suka menggunakan pengambil statis yang dipanggil inst
misalnya:
// 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");
}
contoh penggunaan:
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));