Как вы строите синглтон в дартс?
Одноэлементный шаблон обеспечивает создание только одного экземпляра класса. Как мне построить это в дартс?
29 ответов
Благодаря фабричным конструкторам Dart легко создать синглтон:
class Singleton {
static final Singleton _singleton = new Singleton._internal();
factory Singleton() {
return _singleton;
}
Singleton._internal();
}
Вы можете построить это с new
main() {
var s1 = new Singleton();
var s2 = new Singleton();
print(identical(s1, s2)); // true
print(s1 == s2); // true
}
Вот сравнение нескольких разных способов создания синглтона в Dart.
1. Фабричный конструктор
class SingletonOne {
SingletonOne._privateConstructor();
static final SingletonOne _instance = SingletonOne._privateConstructor();
factory SingletonOne(){
return _instance;
}
}
2. Статическое поле с геттером
class SingletonTwo {
SingletonTwo._privateConstructor();
static final SingletonTwo _instance = SingletonTwo._privateConstructor();
static SingletonTwo get instance { return _instance;}
}
3. Статическое поле
class SingletonThree {
SingletonThree._privateConstructor();
static final SingletonThree instance = SingletonThree._privateConstructor();
}
Как создать экземпляр
Вышеуказанные синглтоны создаются следующим образом:
SingletonOne one = SingletonOne();
SingletonTwo two = SingletonTwo.instance;
SingletonThree three = SingletonThree.instance;
Замечания:
Первоначально я задал это как вопрос, но обнаружил, что все методы, перечисленные выше, действительны и выбор во многом зависит от личных предпочтений.
Вот простой ответ:
class Singleton {
static Singleton _instance;
Singleton._();
static Singleton get getInstance => _instance = _instance ?? Singleton._();
}
Я не нахожу это очень интуитивным чтением нового Singleton(). Вы должны прочитать документы, чтобы знать, что new на самом деле не создает новый экземпляр, как обычно.
Вот еще один способ делать одиночные игры (в основном то, что Эндрю сказал выше).
Библиотека / 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.
Если вы предпочитаете, вы можете также реализовать синглтоны в качестве статического геттера для синглтон-класса. то есть 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);
}
Или это осуждается?
Шаблон синглтона необходим в Java, где концепция глобалов не существует, но, похоже, вам не нужно проделывать долгий путь в Dart.
Вот еще один возможный способ:
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, 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}
Дартс синглтон от const constructor & factory
class Singleton {
factory Singleton() =>
const Singleton._internal_();
const Singleton._internal_();
}
void main() {
print(new Singleton() == new Singleton());
print(identical(new Singleton() , new Singleton()));
}
В этом примере я делаю другие вещи, которые также необходимы, когда я хочу использовать синглтон. Например:
- передать значение конструктору синглтона
- инициализировать значение внутри самого конструктора
- установить значение переменной Singleton
- иметь возможность получать доступ к этим значениям .
Так:
class MySingleton {
static final MySingleton _singleton = MySingleton._internal();
String _valueToBeSet;
String _valueAlreadyInSingleton;
String _passedValueInContructor;
get getValueToBeSet => _valueToBeSet;
get getValueAlreadyInSingleton => _valueAlreadyInSingleton;
get getPassedValueInConstructor => _passedValueInContructor;
void setValue(newValue) {
_valueToBeSet = newValue;
}
factory MySingleton(String passedString) {
_singleton._valueAlreadyInSingleton = "foo";
_singleton._passedValueInContructor = passedString;
return _singleton;
}
MySingleton._internal();
}
Использование MySingleton:
void main() {
MySingleton mySingleton = MySingleton("passedString");
mySingleton.setValue("setValue");
print(mySingleton.getPassedValueInConstructor);
print(mySingleton.getValueToBeSet);
print(mySingleton.getValueAlreadyInSingleton);
}
С
Dart 2.13
версия, это очень просто с
late
ключевое слово.
Late
ключевое слово позволяет нам лениво создавать экземпляры объектов.
В качестве примера вы можете увидеть это:
class LazySingletonExample {
LazySingletonExample._() {
print('instance created.');
}
static late final LazySingletonExample instance = LazySingletonExample._();
}
Примечание: имейте в виду, что он будет создан только один раз, когда вы вызовете lazy
instance
поле.
Прочитав все альтернативы, я пришел к такому выводу, который напоминает мне "классический синглтон":
class AccountService {
static final _instance = AccountService._internal();
AccountService._internal();
static AccountService getInstance() {
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();
}
Вот краткий пример, который объединяет другие решения. Доступ к синглтону может быть сделан:
- Используя
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
}
Модифицированный ответ @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';
Если вы используете Flutter и
provider
пакет для управления состоянием, создание и использование синглтона довольно просто.
- Создать экземпляр
void main() { runApp( MultiProvider( providers: [ ChangeNotifierProvider(create: (context) => SomeModel()), Provider(create: (context) => SomeClassToBeUsedAsSingleton()), ], child: MyApp(), ), ); }
- Получить экземпляр
Widget build(BuildContext context) { var instance = Provider.of<SomeClassToBeUsedAsSingleton>(context); ...
Singleton
объекты могут быть лучше созданы с помощью нулевого оператора безопасности и конструктора фабрики.
class Singleton {
static Singleton? _instance;
Singleton._internal();
factory Singleton() => _instance ?? Singleton._internal();
void someMethod() {
print("someMethod Called");
}
}
Использование:
void main() {
Singleton object = Singleton();
object.someMethod(); /// Output: someMethod Called
}
Примечание:
??
является оператором с нулевым значением, он возвращает правое значение, если левое значение равно нулю, что означает в нашем примере
_instance ?? Singleton._internal();
,
Singleton._internal()
будет возвращен в первый раз, когда объект будет вызван, отдых
_instance
будет возвращен.
** Парадигма Сиглтона в Dart Sound Null Safety**
Этот фрагмент кода показывает, как реализовать синглтон в dart. Обычно это используется в тех ситуациях, когда мы должны использовать один и тот же объект класса каждый раз, например. в транзакциях базы данных.
class MySingleton {
static MySingleton? _instance;
MySingleton._internal();
factory MySingleton() {
if (_instance == null) {
_instance = MySingleton._internal();
}
return _instance!;
}
}
как создать одноэлементный экземпляр класса в дротиках флаттера
class ContactBook {
ContactBook._sharedInstance();
static final ContactBook _shared = ContactBook._sharedInstance();
factory ContactBook() => _shared;
}
Это мой способ сделать синглтон, который принимает параметры (вы можете вставить это прямо на https://dartpad.dev/ ):
void main() {
Logger x = Logger('asd');
Logger y = Logger('xyz');
x.display('Hello');
y.display('Hello There');
}
class Logger{
Logger._(this.message);
final String message;
static Logger _instance = Logger._('??!?*');
factory Logger(String message){
if(_instance.message=='??!?*'){
_instance = Logger._(message);
}
return _instance;
}
void display(String prefix){
print(prefix+' '+message);
}
}
Какие входы:
Hello asd
Hello There asd
'??!?*', который вы видите, - это просто обходной путь, который я сделал для временной инициализации переменной _instance, не делая ее регистратором? тип (нулевой безопасности).
Привет, как насчет этого? Очень простая реализация, сам Injector является синглтоном и также добавляет в него классы. Конечно, может быть продлен очень легко. Если вы ищете что-то более сложное, проверьте этот пакет: 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;
}
}
Поскольку я не очень люблю использовать 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));
Это должно работать.
class GlobalStore {
static GlobalStore _instance;
static GlobalStore get instance {
if(_instance == null)
_instance = new GlobalStore()._();
return _instance;
}
_(){
}
factory GlobalStore()=> instance;
}
Вот так просто:
class Singleton {
Singleton._();
static final _instance = Singleton._();
factory Singleton() => _instance;
}
void singleton() {
final mySingleton = Singleton();
}
Создать синглтон
class PermissionSettingService {
static PermissionSettingService _singleton = PermissionSettingService._internal();
factory PermissionSettingService() {
return _singleton;
}
PermissionSettingService._internal();
}
Сбросить синглтон
// add this function inside the function
void reset() {
_singleton = PermissionSettingService._internal();
}
Это также способ создания класса Singleton
class Singleton{
Singleton._();
static final Singleton db = Singleton._();
}
В создании синглтона в Dart нет ничего сложного. Вы можете объявить любую переменную в расположении верхнего уровня (глобальном), которое по умолчанию является синглтоном. Вы также можете объявить переменную как статический член класса. Это синглтон.
class A {}
final a = A();
Однако сказанное выше не позволяет заменить экземпляр для тестирования. Другая проблема заключается в том, что по мере усложнения приложения вам может понадобиться преобразовать глобальные или статические переменные во временные зависимости внутри ваших классов. Если вы используете внедрение зависимостей, вы можете изменить зависимость внутри своей композиции в любое время. Это пример использования ioc_container для настройки одноэлементного экземпляра в корне приложения. Вы можете изменить это на временную зависимость в любое время, используяadd
вместоaddSingletonService
import 'package:ioc_container/ioc_container.dart';
class A {}
void main(List<String> arguments) {
final builder = IocContainerBuilder()..addSingletonService(A());
final container = builder.toContainer();
final a1 = container<A>();
final a2 = container<A>();
print(identical(a1, a2));
}
Вышеуказанные отпечаткиtrue
потому что приложение будет чеканить только один экземплярA
.
Я использую этот простой паттерн на дротике и ранее на Swift. Мне нравится, что он лаконичен и использует только один способ.
class Singleton {
static Singleton shared = Singleton._init();
Singleton._init() {
// init work here
}
void doSomething() {
}
}
Singleton.shared.doSomething();
Во Flutter одноэлементный класс — это класс, который позволяет вам создавать только один экземпляр за время существования вашего приложения. Синглтоны часто используются для управления глобальным состоянием или для обеспечения существования только одного экземпляра определенного класса. Вы можете реализовать синглтон во Flutter, используя следующий шаблон кода:
class MySingleton {
// Private constructor
MySingleton._privateConstructor();
// The single instance of the class
static final MySingleton _instance = MySingleton._privateConstructor();
// Factory constructor to provide access to the instance
factory MySingleton() {
return _instance;
}
// Add your methods and properties here
}
void main() {
MySingleton instance1 = MySingleton();
MySingleton instance2 = MySingleton();
print(identical(instance1, instance2)); // This will print 'true' since it's the same instance.
}
Вы можете просто использовать конструкторы Constant.
class Singleton {
const Singleton(); //Constant constructor
void hello() { print('Hello world'); }
}
Пример:
Singleton s = const Singleton();
s.hello(); //Hello world
Согласно документации:
Постоянные конструкторы
Если ваш класс создает объекты, которые никогда не меняются, вы можете сделать эти объекты константами времени компиляции. Для этого определите конструктор const и убедитесь, что все переменные экземпляра являются final.