Шаблон 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));