Однотонний візерунок забезпечує лише один екземпляр класу. Як я будую це в 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
Ключове слово передбачає , що клас конкретизується, що це не так . Я б пішов на статичний метод get()
або getInstance()
як я роблю в Java.
Singleton._internal();
, схожий на виклик методу, коли це дійсно визначення конструктора. Там _internal
назва. І є прекрасний мовний дизайн, який дозволяє Дартру запускати (виривати?) За допомогою звичайного конструктора, а потім, якщо потрібно, змінювати його на 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');
}
}
головна.дарт
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);
}
Або це нахмурилося?
Однотонний візерунок необхідний на Java, де поняття глобалів не існує, але, схоже, вам не потрібно було б пройти довгий шлях у Дартсі.
Singleton
. У моєму прикладі вище, Singleton
клас - це справжній сингтон, в ізоляті Singleton
коли-небудь може існувати лише один екземпляр .
new Singleton._internal()
стільки разів, скільки хоче, створюючи безліч об'єктів Singleton
класу. Якби Impl
клас на прикладі Ендрю був приватним ( _Impl
), він був би таким самим, як і ваш приклад. З іншого боку, одиночний антипатерн, і ніхто його не повинен використовувати.
Singelton._internal()
. Ви можете стверджувати, що розробники класу singelton також могли кілька разів інсталювати клас. Впевнений, що є 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, 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}
Модифікована @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;
Ось стислий приклад, який поєднує інші рішення. Доступ до синглтону можна зробити:
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_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 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));