Шаблон singleton гарантирует, что когда-либо будет создан только один экземпляр класса. Как мне построить это в Dart?
Шаблон singleton гарантирует, что когда-либо будет создан только один экземпляр класса. Как мне построить это в 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Ключевое слово предполагает , что класс конкретизируется, что это не так . Я бы выбрал статический метод get()или getInstance()как в Java.
Singleton._internal();который выглядит как вызов метода, когда на самом деле это определение конструктора. Это _internalимя. И есть изящный дизайн языка, который Dart позволяет вам начать (выскочить?), Используя обычный конструктор, а затем, если необходимо, изменить его на 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();
}
Вышеупомянутые синглтоны создаются следующим образом:
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();
}
Обратите внимание, что синглтон не создается до первого вызова метода получения из-за ленивой инициализации Дарта.
Если вы предпочитаете, вы также можете реализовать синглтоны в качестве статического получателя в одноэлементном классе. т.е. Thing.singletonвместо геттера верхнего уровня.
Также прочтите взгляд Боба Нистрома на синглтоны из его книги паттернов программирования игр .
Как насчет того, чтобы просто использовать глобальную переменную в вашей библиотеке, например?
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);
}
Или это не одобряют?
Шаблон singleton необходим в Java, где не существует концепции глобальных объектов, но похоже, что вам не нужно долго обходить Dart.
Singletonдоступ к экземпляру . В моем примере выше Singletonкласс является настоящим синглтоном, только один экземпляр Singletonможет существовать в изоляте.
new Singleton._internal()сколько угодно раз, создавая множество объектов Singletonкласса. Если бы Implкласс в примере Эндрю был private ( _Impl), он был бы таким же, как ваш пример. С другой стороны, синглтон - это антипаттерн, и никому не следует его использовать.
Singelton._internal(). Вы можете возразить, что разработчики класса singleton также могут инсталлировать класс несколько раз. Конечно, есть 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;
}
Синглтон, который не может изменить объект после создания экземпляра
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, age: 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}
Прочитав все альтернативы, я пришел к такому выводу, который напоминает мне «классический синглтон»:
class AccountService {
static final _instance = AccountService._internal();
AccountService._internal();
static AccountService getInstance() {
return _instance;
}
}
getInstanceметод в таком instanceсвойстве:static AccountService get instance => _instance;
Измененный ответ @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';
Вот краткий пример, объединяющий другие решения. Доступ к синглтону можно выполнить:
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
}
Привет, а как насчет этого? Очень простая реализация, Инжектор сам по себе синглтон, а также добавил в него классы. Конечно, можно очень легко расширить. Если вы ищете что-то более сложное, проверьте этот пакет: 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;
}
}
Вот как я использую синглтон в своих проектах
class FooAPI {
foo() {
// some async func to api
}
}
class SingletonService {
FooAPI _fooAPI;
static final SingletonService _instance = SingletonService._internal();
static SingletonService instance = SingletonService();
factory SingletonService() {
return _instance;
}
SingletonService._internal() {
// TODO: add init logic if needed
// FOR EXAMPLE API parameters
}
void foo() async {
await _fooAPI.foo();
}
}
void main(){
SingletonService.instance.foo();
}
Это должно работать.
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ключевое слово или другой конструктор, например вызовы синглтонов, я бы предпочел использовать статический метод 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));