Dart'da nasıl Singleton inşa edersin?


205

Tekli desen, bir sınıfın yalnızca bir örneğinin oluşturulmasını sağlar. Bunu Dart'da nasıl oluşturabilirim?


Aşağıda bir sınıf single'ı yapmanın çeşitli yollarını açıklayan birkaç cevap gördüm. Bu yüzden neden bu class_name nesnesini sevmediğimizi düşünüyorum; if (object == null) dönüş nesnesi = new class_name; başka dönüş nesnesi
Avnish kumar

Yanıtlar:


326

Dart'ın fabrika inşaatçıları sayesinde , tek birton inşa etmek kolaydır:

class Singleton {
  static final Singleton _singleton = Singleton._internal();

  factory Singleton() {
    return _singleton;
  }

  Singleton._internal();
}

Bu şekilde inşa edebilirsiniz

main() {
  var s1 = Singleton();
  var s2 = Singleton();
  print(identical(s1, s2));  // true
  print(s1 == s2);           // true
}

2
Her ne kadar iki kez somutlaştırmanın anlamı nedir? İkinci kez başlattığınızda bir hata atmak daha iyi olmaz mı?
westoque

53
İki kez başlatmıyorum, sadece bir Singleton nesnesine iki kez referans alıyorum. Muhtemelen gerçek hayatta üst üste iki kez yapmazsınız :) İstisna atılmasını istemem, her "new Singleton ()" dediğimde aynı singleton örneğini istiyorum. İtiraf ediyorum, biraz kafa karıştırıcı ... newburada "yeni bir tane inşa et" anlamına gelmiyor, sadece "kurucuyu çalıştır" diyor.
Seth Ladd

1
Fabrika anahtar kelimesi tam olarak burada ne işe yarıyor? Tamamen uygulamaya açıklama ekliyor. Neden gerekli?
Καrτhικ

4
Örneği almak için bir kurucu kullanmanız biraz kafa karıştırıcı. newAnahtar kelime sınıfı ki değil, örneği göstermektedir. Statik bir yöntem get()veya getInstance()Java'da yaptığım gibi giderdim .
Steven Roose

11
@SethLadd bu çok güzel ama birkaç açıklama noktasına ihtiyacı olduğunu düşünüyorum. Singleton._internal();Gerçekten bir yapıcı tanımı olduğunda bir yöntem çağrısına benzeyen garip sözdizimi var. Orada _internalisim. Ve Dart'ın sıradan bir kurucu kullanarak başlamanıza (dart?) İzin vermesi ve ardından gerekirse factorytüm arayanları değiştirmeden bir yönteme değiştirmesine izin veren şık bir dil tasarım noktası var .
Jerry101

174

Dart'da bir singleton oluşturmanın birkaç farklı yolunun karşılaştırması.

1. fabrika yapıcı

class SingletonOne {

  SingletonOne._privateConstructor();

  static final SingletonOne _instance = SingletonOne._privateConstructor();

  factory SingletonOne() {
    return _instance;
  }

}

2. alıcı ile statik alan

class SingletonTwo {

  SingletonTwo._privateConstructor();

  static final SingletonTwo _instance = SingletonTwo._privateConstructor();

  static SingletonTwo get instance => _instance;
  
}

3. Statik alan

class SingletonThree {

  SingletonThree._privateConstructor();

  static final SingletonThree instance = SingletonThree._privateConstructor();
  
}

Nasıl instanstiate

Yukarıdaki singletonlar şu şekilde başlatılır:

SingletonOne one = SingletonOne();
SingletonTwo two = SingletonTwo.instance;
SingletonThree three = SingletonThree.instance;

Not:

Aslında bunu bir soru olarak sordum , ancak yukarıdaki tüm yöntemlerin geçerli olduğunu ve seçimin büyük ölçüde kişisel tercihlere bağlı olduğunu keşfettim.


3
Sadece cevabını iptal ettim. Kabul edilen cevaptan çok daha açık. Sadece bir soru daha: ikinci ve üçüncü yol için, özel kurucunun anlamı nedir? Birçok insanın bunu yaptığını gördüm, ama anlamıyorum. Ben her zaman kullanırım static final SingletonThree instance = SingletonThree(). Aynı şey ikinci yoldan gider _instance. Özel bir kurucu kullanmamanın dezavantajını bilmiyorum. Şimdiye kadar, yolumda herhangi bir sorun bulamıyorum. İkinci ve üçüncü yollar, zaten varsayılan kurucuya olan çağrıyı engellemez.
sgon00

3
@ sgon00, özel kurucu böylece başka bir örnek yapamazsınız. Aksi halde herkes yapabilirdi SingletonThree instance2 = SingletonThree(). Özel bir kurucu olduğunda bunu yapmaya çalışırsanız, hatayı alırsınız:The class 'SingletonThree' doesn't have a default constructor.
Suragch

41

Çok sezgisel bir okuma bulamıyorum new Singleton(). newNormalde olduğu gibi yeni bir örnek oluşturmadığını bilmek için belgeleri okumalısınız .

İşte singleton yapmak için başka bir yol (temelde Andrew yukarıda söylediğini).

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();
}

Dart'ın tembel başlatması nedeniyle alıcı ilk kez çağrılıncaya kadar singletonun oluşturulmadığını unutmayın.

İsterseniz, singletonları singleton sınıfında statik alıcı olarak da uygulayabilirsiniz. yani Thing.singleton, bir üst düzey alıcı yerine.

Ayrıca Bob Nystrom'un Oyun programlama kalıpları kitabından singletonları ele geçirmesini okuyun .


1
Greg ve üst düzey dart özelliği sayesinde bu benim için daha anlamlı.
Eason PI

Bu deyimsel değil. Dilde tek bir desen oluşturmak bir rüya özelliğidir ve alışkın olmadığınız için atıyorsunuz.
Arash

1
Seth'in örneği ve bu örnek tekil kalıplardır. Bu gerçekten "new Singleton ()" ve "singleton" sözdizimi meselesidir. İkincisini daha net buluyorum. Dart'ın fabrika inşaatçıları faydalıdır, ancak bunun onlar için iyi bir kullanım örneği olduğunu düşünmüyorum. Dart'ın tembel başlatılmasının da az kullanılan harika bir özellik olduğunu düşünüyorum. Ayrıca Bob'un yukarıdaki makalesini okuyun - çoğu durumda singletonlara karşı öneride bulunur.
Greg Lowe

Bu konuyu posta listesinden de okumanızı tavsiye ederim. groups.google.com/a/dartlang.org/d/msg/misc/9dFnchCT4kA/…
Greg Lowe

Bu çok daha iyi. "Yeni" anahtar kelimesi, büyük ölçüde yeni bir nesnenin inşasını ima eder. Kabul edilen çözüm gerçekten yanlış geliyor.
Sava B.

16

Kütüphanenizde sadece global bir değişken kullanmaya ne dersiniz?

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);
}

Yoksa kaşlarını çattı mı?

Tekton paterni, küresel kavramının olmadığı Java'da gereklidir, ancak Dart'da uzun süre dolaşmanıza gerek yok gibi görünüyor.


5
Üst düzey değişkenler iyidir. Ancak, single.dart dosyasını içe aktarabilen herkes "yeni Impl ()" oluşturmakta özgürdür. Impl'e bir alt çizgi yapıcısı verebilirsiniz, ancak daha sonra singleton kütüphanesinin içindeki kod bu yapıcı olarak adlandırılabilir.
Seth Ladd

Ve uygulamanızdaki kod yapamıyor? Cevabınızda neden üst düzey bir değişkenden daha iyi olduğunu açıklayabilir misiniz?
Ocak

2
Merhaba @Jan, daha iyi veya daha kötü değil, sadece farklı. Andrew örneğinde, Impl tek bir sınıf değildir. Örneğe Singletonerişimi kolaylaştırmak için üst düzey bir değişkeni doğru şekilde kullandı . Yukarıdaki Singletonörneğimde, sınıf gerçek bir single'tır, Singletonizolatta yalnızca bir örneği bulunabilir.
Seth Ladd

1
Seth, haklı değilsin. Orada hiçbir sınıfın instantiability kısıtlayan hiçbir yolu olmadığı için gerçek bir singleton'ununu inşa etmek Dart yol, içeride ilan kütüphanede. Her zaman kütüphane yazarından disiplin gerektirir. Örneğinizde, bildiren kütüphane new Singleton._internal()istediği kadar arayabilir ve Singletonsınıfın birçok nesnesini oluşturabilir . Eğer ImplAndrew örnekte sınıf (özel olarak _Impl), bu örnekte olduğu gibi aynı olacaktır. Öte yandan, singleton bir antipatterndir ve kimse onu yine de kullanmamalıdır.
Ladicek

@Ladicek, bir kütüphanenin geliştiricilerine yeni dememek için güvenmiyor musunuz Singelton._internal()? Singelton sınıfının geliştiricilerinin sınıfı birkaç kez de yaratabileceğini iddia edebilirsiniz. Tabii ki enum singelton var ama bana göre sadece teorik kullanım. Bir enum bir tekil değil, bir enum ... Üst düzey değişkenlerin kullanımına gelince (@Andrew ve @Seth): Üst düzey değişkene kimse yazılamadı mı? Hiçbir şekilde korunmuyor mu, yoksa bir şey mi kaçırıyorum?
Tobias Ritzau

12

İşte olası başka bir yol:

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;
}

11

Const yapıcı ve fabrika tarafından dart singleton

class Singleton {
  factory Singleton() =>
    const Singleton._internal_();
  const Singleton._internal_();
}


void main() {
  print(new Singleton() == new Singleton());
  print(identical(new Singleton() , new Singleton()));
}

5

Örnekten sonra nesneyi değiştiremeyen 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}

4

Swift tarzı singleton'u kimin tercih ettiği için @Seth Ladd cevabı değiştirildi .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;
}

Örneklem:

Auth.shared.username = 'abc';

4

Tüm alternatifleri okuduktan sonra bana bir "klasik singleton" hatırlatıyor:

class AccountService {
  static final _instance = AccountService._internal();

  AccountService._internal();

  static AccountService getInstance() {
    return _instance;
  }
}

3
Böyle getInstancebir instanceözellikte yöntemi değiştirmek istiyorum:static AccountService get instance => _instance;
gianlucaparadise

Bunu severim. çünkü örnek döndürülür ve diğer yöntemler kullanılmadan önce bir şey eklemek istiyorum.
chitgoks

4

İşte basit bir cevap:

class Singleton {
  static Singleton _instance;

  Singleton._();

  static Singleton get getInstance => _instance = _instance ?? Singleton._();
}

3

İşte diğer çözümleri birleştiren kısa bir örnek. Singleton'a erişim şu şekilde yapılabilir:

  • Örneği gösteren singletongenel bir değişken kullanma .
  • Ortak Singleton.instanceörüntü.
  • Örneği döndüren bir fabrika olan varsayılan kurucuyu kullanma.

Not: singleton'u kullanan kodun tutarlı olması için üç seçenekten yalnızca birini uygulamanız gerekir.

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;
}

Karmaşık başlatma yapmanız gerekiyorsa, örneği programda daha sonra kullanmadan önce yapmanız gerekir.

Misal

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
}

1

Merhaba, böyle bir şeye ne dersin? Çok basit uygulama, Enjektör kendisi singleton ve ayrıca içine sınıflar ekledi. Tabii ki çok kolay genişletilebilir. Daha sofistike bir şey arıyorsanız bu paketi kontrol edin: 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;
  }
}

0

Bu çalışmalı.

class GlobalStore {
    static GlobalStore _instance;
    static GlobalStore get instance {
       if(_instance == null)
           _instance = new GlobalStore()._();
       return _instance;
    }

    _(){

    }
    factory GlobalStore()=> instance;


}

Lütfen takip sorularını cevap olarak göndermeyin. Bu kodla ilgili sorun biraz ayrıntılı olmasıdır. static GlobalStore get instance => _instance ??= new GlobalStore._();yapardım. Ne _(){}yapmalı? Bu gereksiz görünüyor.
Günter Zöchbauer

üzgünüm, bu bir öneriydi, takip sorusu değil, _ () {} özel bir kurucu yaratacak değil mi?
Vilsad PP

Yapıcılar sınıf adıyla başlar. Bu, yalnızca bir dönüş türü belirtilmemiş normal bir özel yönetim ortamı yöntemidir.
Günter Zöchbauer

1
Downvote için üzgünüm, ancak kalitesiz olduğunu ve mevcut cevaplara ek olarak herhangi bir değer katmadığını düşünüyorum.
Günter Zöchbauer

2
Bu kod soruyu cevaplayabilirken, sorunun nasıl ve / veya neden çözüldüğüne ilişkin ek bağlam sağlamak yanıtlayıcının uzun vadeli değerini artıracaktır.
Karl Richter

0

newAnahtar kelime veya singleton çağrıları gibi başka bir kurucu kullanmak çok sevmiyorum gibi, instörneğin denilen statik bir alıcı kullanmayı tercih ederim :

// 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");
}

örnek kullanım:

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));
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.