รูปแบบซิงเกิลทำให้มั่นใจได้ว่ามีเพียงคลาสเดียวเท่านั้นที่ถูกสร้างขึ้น ฉันจะสร้างสิ่งนี้ใน Dart ได้อย่างไร
รูปแบบซิงเกิลทำให้มั่นใจได้ว่ามีเพียงคลาสเดียวเท่านั้นที่ถูกสร้างขึ้น ฉันจะสร้างสิ่งนี้ใน Dart ได้อย่างไร
คำตอบ:
ต้องขอบคุณผู้สร้างโรงงานของ Dart ทำให้สามารถสร้างซิงเกิลตันได้ง่าย:
class Singleton {
static final Singleton _singleton = Singleton._internal();
factory Singleton() {
return _singleton;
}
Singleton._internal();
}
คุณสามารถสร้างมันได้เช่นนี้
main() {
var s1 = Singleton();
var s2 = Singleton();
print(identical(s1, s2)); // true
print(s1 == s2); // true
}
new
ไม่ได้หมายความว่า "สร้างใหม่" ที่นี่เพียงแค่บอกว่า "เรียกใช้ตัวสร้าง"
new
คำหลักที่แสดงให้เห็นว่าชั้นจะ instantiated ซึ่งมันไม่ได้เป็น ฉันจะใช้วิธีแบบคงที่get()
หรือgetInstance()
เหมือนที่ฉันทำใน Java
Singleton._internal();
ที่ดูเหมือนว่าการเรียกเมธอดเมื่อนิยามคอนสตรัคเตอร์จริงๆ มี_internal
ชื่อ และมีจุดออกแบบภาษาที่ดีที่ Dart ให้คุณเริ่มต้น (dart out?) โดยใช้ Constructor ธรรมดาจากนั้นหากจำเป็นให้เปลี่ยนเป็นfactory
วิธีการโดยไม่ต้องเปลี่ยนผู้โทรทั้งหมด
นี่คือการเปรียบเทียบวิธีต่างๆในการสร้างซิงเกิลตันใน 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();
}
Singletons ด้านบนเป็นอินสแตนซ์นี้:
SingletonOne one = SingletonOne();
SingletonTwo two = SingletonTwo.instance;
SingletonThree three = SingletonThree.instance;
บันทึก:
ฉันแรกเริ่มถามคำถามนี้ แต่พบว่าวิธีการทั้งหมดข้างต้นนั้นถูกต้องและตัวเลือกส่วนใหญ่ขึ้นอยู่กับความชอบส่วนตัว
static final SingletonThree instance = SingletonThree()
เสมอ _instance
เดียวกันจะไปถึงวิธีการที่สองสำหรับ ฉันไม่รู้ว่าอะไรคือข้อเสียของการไม่ใช้คอนสตรัคเตอร์ส่วนตัว จนถึงตอนนี้ฉันไม่พบปัญหาใด ๆ ในทางของฉัน วิธีที่สองและสามไม่ได้ปิดกั้นการเรียกไปยังตัวสร้างเริ่มต้นอย่างไรก็ตาม
SingletonThree instance2 = SingletonThree()
มิฉะนั้นทุกคนสามารถทำได้ หากคุณพยายามทำเช่นนี้เมื่อมีคอนสตรัคส่วนตัวคุณจะได้รับข้อผิดพลาด:The class 'SingletonThree' doesn't have a default constructor.
new Singleton()
ผมพบว่ามันไม่อ่านง่ายมาก คุณต้องอ่านเอกสารเพื่อที่จะรู้ว่าnew
ว่าไม่ได้สร้างอินสแตนซ์ใหม่ตามปกติ
นี่เป็นอีกวิธีในการทำซิงเกิลตัน (โดยพื้นฐานแล้วแอนดรูว์พูดไว้ข้างต้น)
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();
}
โปรดทราบว่าซิงเกิลตันจะไม่ถูกสร้างขึ้นจนกว่าจะมีการเรียกใช้ getter เป็นครั้งแรกเนื่องจากการเริ่มต้นที่ขี้เกียจของ Dart
หากคุณต้องการคุณยังสามารถใช้ singletons เป็น static getter ในคลาส singleton กล่าวคือThing.singleton
แทนที่จะทะเยอทะยานระดับบนสุด
นอกจากนี้อ่านเอาบ๊อบ Nystrom บนsingletons จากเกมหนังสือรูปแบบการเขียนโปรแกรมของเขา
แล้วแค่ใช้ตัวแปรโกลบอลภายในห้องสมุดของคุณล่ะ?
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);
}
หรือเรื่องนี้ขมวดคิ้วอยู่?
รูปแบบซิงเกิลเป็นสิ่งจำเป็นใน Java ที่ไม่มีแนวคิดของ globals แต่ดูเหมือนว่าคุณไม่จำเป็นต้องไปไกลใน Dart
Singleton
เพื่อให้เข้าถึงได้ง่าย ในตัวอย่างของฉันด้านบนSingleton
คลาสเป็นซิงเกิลจริงมีเพียงอินสแตนซ์เดียวเท่านั้นที่Singleton
สามารถมีอยู่ในไอโซเลท
new Singleton._internal()
บ่อยเท่าที่ต้องการสร้างวัตถุจำนวนมากในSingleton
ชั้นเรียน หากImpl
คลาสในตัวอย่างของแอนดรูว์เป็นแบบส่วนตัว ( _Impl
) มันจะเหมือนกับตัวอย่างของคุณ ในทางตรงข้ามซิงเกิลตันเป็นปฏิปักษ์และไม่มีใครควรใช้มันอย่างไรก็ตาม
Singelton._internal()
คุณไม่ไว้วางใจนักพัฒนาของห้องสมุดจะไม่เรียกใหม่ คุณสามารถยืนยันได้ว่านักพัฒนาของชั้น Singelton สามารถติดตั้งชั้นเรียนหลายครั้งเช่นกัน แน่นอนว่ามี enum singelton แต่สำหรับฉันแล้วมันเป็นเพียงการใช้ตามหลักวิชาเท่านั้น enum เป็น enum ไม่ใช่ singelton ... สำหรับการใช้ตัวแปรระดับบนสุด (@Andrew และ @Seth): ทุกคนไม่สามารถเขียนถึงตัวแปรระดับบนสุดได้หรือไม่ มันไม่ได้รับการปกป้องหรือฉันขาดอะไรไป?
นี่เป็นอีกวิธีที่เป็นไปได้:
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;
}
โผ Singleton โดย const คอนสตรัค & โรงงาน
class Singleton {
factory Singleton() =>
const Singleton._internal_();
const Singleton._internal_();
}
void main() {
print(new Singleton() == new Singleton());
print(identical(new Singleton() , new 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}
Modified @Seth Ladd ตอบสำหรับผู้ที่ชอบสไตล์ Swift ของซิงเกิลที่ชอบ.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;
}
ตัวอย่าง:
Auth.shared.username = 'abc';
หลังจากอ่านทางเลือกทั้งหมดแล้วฉันก็พบสิ่งนี้ซึ่งทำให้ฉันนึกถึง "ซิงเกิลคลาสสิค":
class AccountService {
static final _instance = AccountService._internal();
AccountService._internal();
static AccountService getInstance() {
return _instance;
}
}
getInstance
วิธีในinstance
สถานที่ให้บริการเช่นนี้:static AccountService get instance => _instance;
นี่คือคำตอบง่ายๆ:
class Singleton {
static Singleton _instance;
Singleton._();
static Singleton get getInstance => _instance = _instance ?? Singleton._();
}
นี่คือตัวอย่างย่อที่รวมโซลูชันอื่น ๆ การเข้าถึงซิงเกิลสามารถทำได้โดย:
singleton
ตัวแปรโกลบอลที่ชี้ไปยังอินสแตนซ์Singleton.instance
รูปแบบทั่วไปหมายเหตุ:คุณควรใช้เพียงหนึ่งในสามตัวเลือกเพื่อให้รหัสที่ใช้ซิงเกิลตันสอดคล้องกัน
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;
}
หากคุณจำเป็นต้องทำการเริ่มต้นที่ซับซ้อนคุณจะต้องทำก่อนที่จะใช้อินสแตนซ์ในภายหลังในโปรแกรม
ตัวอย่าง
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
}
สวัสดีแล้วเรื่องแบบนี้ล่ะ การใช้งานที่ง่ายมาก Injector นั้นเป็นซิงเกิลตันและยังเพิ่มคลาสเข้าไปอีกด้วย แน่นอนสามารถขยายได้อย่างง่ายดายมาก หากคุณกำลังมองหาบางสิ่งที่ซับซ้อนยิ่งขึ้นลองตรวจสอบแพ็คเกจนี้: https://pub.dartlang.org/packages/flutter_simple_dependency_inject
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;
}
}
สิ่งนี้น่าจะใช้ได้
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._();
จะทำ. สิ่งที่_(){}
ควรทำ ดูเหมือนว่าซ้ำซ้อน
เนื่องจากฉันไม่ชอบการใช้new
คำหลักหรือตัวสร้างอื่น ๆ เช่นการโทรบนซิงเกิลฉันจึงต้องการใช้ getter แบบสแตติกที่เรียกinst
เช่น:
// 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");
}
ตัวอย่างการใช้งาน:
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));