ダートでシングルトンをどのように構築しますか?


203

シングルトンパターンは、クラスのインスタンスが1つだけ作成されることを保証します。Dartでこれを構築するにはどうすればよいですか?


クラスシングルトンを作成するためのいくつかの方法を説明する以下のいくつかの回答を見ました。それで、なぜこのclass_nameオブジェクトのようにしないのかを考えています。if(object == null)return object = new class_name; それ以外の場合はオブジェクトを返す
Avnish kumar

回答:


321

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
}

2
それを2回インスタンス化する意味は何ですか?2回目にインスタンス化するときにエラーが発生した方がいいのではないでしょうか。
西海岸の

53
2回インスタンス化するのではなく、Singletonオブジェクトへの参照を2回取得するだけです。おそらく、実際には2回続けてそれを実行することはないでしょう:)例外がスローされるのは望ましくありません。 "new Singleton()"と言うたびに、同じシングルトンインスタンスが必要です。少しわかりにくいnewかもしれませんが、ここでは「新しいコンストラクタを作成する」という意味ではなく、「コンストラクタを実行する」とだけ言っています。
セス・ラッド

1
ここでfactoryキーワードは正確に何を提供しますか?これは純粋に実装に注釈を付けています。なぜそれが必要なのですか?
Καrτhικ

4
インスタンスを取得するためにコンストラクターを使用しているのは、ちょっと混乱しています。newキーワードは、それがされていない、クラスをインスタンス化することを示唆しています。私は、静的メソッドのために行くだろうget()か、getInstance()私はJavaで行うように。
Steven Roose 2014年

11
@SethLaddこれはとてもいいですが、いくつか説明が必要です。Singleton._internal();それが実際にコンストラクター定義である場合、メソッド呼び出しのように見える奇妙な構文があります。_internal名前があります。そして、Dartが通常のコンストラクターを使用して開始(dart out?)し、必要に応じて、factoryすべての呼び出し元を変更せずにメソッドに変更できる、気の利いた言語設計ポイントがあります。
Jerry101 2014年

169

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

3.静的フィールド

class SingletonThree {

  SingletonThree._privateConstructor();

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

インスタンス化する方法

上記のシングルトンは次のようにインスタンス化されます。

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

注意:

私はもともとこれを質問として尋ねましたが、上記の方法はすべて有効であり、選択は個人の好みに大きく依存することがわかりました。


3
私はあなたの答えに賛成しました。受け入れられた答えよりもはるかに明確です。もう1つの質問です。2番目と3番目の方法について、プライベートコンストラクターのポイントは何ですか。多くの人がそうしているのを見ましたが、私は要点を理解していません。私はいつも単に使用しますstatic final SingletonThree instance = SingletonThree()。同じことがの2番目の方法にも当てはまり_instanceます。プライベートコンストラクターを使用しないことの欠点は何ですか。これまでのところ、問題はありません。2番目と3番目の方法は、いずれにしてもデフォルトのコンストラクターへの呼び出しをブロックしていません。
sgon00

3
@ sgon00、プライベートコンストラクターは、別のインスタンスを作成できないようにするためのものです。それ以外の場合は誰でもできますSingletonThree instance2 = SingletonThree()。プライベートコンストラクタがある場合には、あなたがこれを行うにしようとすると、エラーになります:The class 'SingletonThree' doesn't have a default constructor.
Suragch

40

直感的に読めないnew Singleton()。あなたはそれを知るためにドキュメントを読む必要がありますnewそれが通常のように実際に新しいインスタンスを作成していないは。

シングルトンを実行する別の方法を以下に示します(基本的に、Andrewが上で言ったこと)。

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の遅延初期化により、最初にゲッターが呼び出されるまで作成されないことに注意してください。

必要に応じて、シングルトンクラスの静的ゲッターとしてシングルトンを実装することもできます。すなわちThing.singleton、トップレベルのゲッターの代わりに。

また、彼のゲームプログラミングパターンの本から、ボブニストロムのシングルトンへの取り組みを読んでください。


1
これは、Gregとダーツのトップレベルプロパティの機能のおかげで、私にはより理にかなっています。
Eason PI、

これは慣用的ではありません。言語でシングルトンパターンを作成することは夢の機能であり、慣れていないために捨てています。
アラッシュ

1
セスの例とこの例はどちらもシングルトンパターンです。これは、実際には、構文「new Singleton()」と「singleton」の問題です。後者の方がより明確です。Dartのファクトリー・コンストラクターは便利ですが、これはそれらの良いユースケースではないと思います。また、ダートの遅延初期化は十分に活用されていない優れた機能だと思います。また、上記のボブの記事を読んでください-ほとんどの場合、シングルトンに対してはお勧めしません。
グレッグロウ2017年

また、メーリングリストでこのスレッドを読むことをお勧めします。groups.google.com/a/dartlang.org/d/msg/misc/9dFnchCT4kA/...
グレッグ・ロウ

これははるかに優れています。「新しい」キーワードは、新しいオブジェクトの構築をかなり強く意味します。受け入れられた解決策は本当に間違っていると感じています。
サバB.

16

ライブラリ内でグローバル変数を使用するだけではどうですか?

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で長い時間を費やす必要はないようです。


5
トップレベルの変数はクールです。ただし、single.dartをインポートできるユーザーなら誰でも「新しいImpl()」を自由に作成できます。Implにアンダースコアコンストラクターを与えることができますが、シングルトンライブラリのコードはそのコンストラクターを呼び出すことができます。
セスラッド2012

そして、あなたの実装のコードはできませんか?トップレベルの変数よりも優れている理由を回答で説明できますか?
2012

2
こんにちは@Jan、それは良くも悪くもありません、ただ違うだけです。Andrewの例では、Implはシングルトンクラスではありません。インスタンスSingletonにアクセスしやすくするために、トップレベルの変数を正しく使用しました。上記の私の例では、Singletonクラスは実際のシングルトンSingletonです。分離内に存在できるインスタンスは1つだけです。
セス・ラッド

1
セス、あなたは正しくない。ありません何のクラスのインスタンス化可能性(instantiability)を制限する方法がないと本当のシングルトンを構築するためのダートでの方法は、内部で宣言するライブラリ。それは常に図書館の作者からの規律を必要とします。あなたの例では、宣言ライブラリは必要なnew Singleton._internal()だけ何度でも呼び出すことができ、Singletonクラスの多くのオブジェクトを作成します。場合はImplアンドリューの例ではクラスが(プライベートだった_Impl)、それはあなたの例と同じになります。一方、シングルトンはアンチパターンであり、誰もがそれを使用するべきではありません。
Ladicek 2012

@Ladicek、あなたは新しいコールするライブラリの開発者ではない信用していませんSingelton._internal()。singeltonクラスの開発者は、クラスを数回インスタンス化することもできると主張できます。確かにenum singeltonはありますが、私にはそれは理論的にのみ使用されます。enumはsingeltonではなくenumです...トップレベルの変数(@Andrewと@Seth)の使用については:誰もトップレベルの変数に書き込むことができませんでしたか?それは決して保護されていません、または私は何かを逃していますか?
Tobias Ritzau 2012年

12

これは別の可能な方法です:

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コンストラクターとファクトリーによるダートシングルトン

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


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

5

インスタンス後にオブジェクトを変更できないシングルトン

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スタイルを好む人のために@Seth Laddの回答を変更しました.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';

4

すべての代替案を読んだ後、私はこれを思い付きました。これは「クラシックシングルトン」を思い出させます。

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

  AccountService._internal();

  static AccountService getInstance() {
    return _instance;
  }
}

3
私はこのようなプロパティのgetInstanceメソッドを変更しinstanceます:static AccountService get instance => _instance;
gianlucaparadise '27 / 10/27

私はこれが好き。インスタンスが返されて他のメソッドが使用される前に何かを追加したいので。
Chitgoks

4

ここに簡単な答えがあります:

class Singleton {
  static Singleton _instance;

  Singleton._();

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

3

以下は、他のソリューションを組み合わせた簡潔な例です。シングルトンへのアクセスは、次の方法で実行できます。

  • singletonインスタンスを指すグローバル変数を使用する。
  • 共通のSingleton.instanceパターン。
  • インスタンスを返すファクトリであるデフォルトのコンストラクタを使用します。

注:シングルトンを使用するコードに一貫性を持たせるために、3つのオプションのうち1つだけを実装する必要があります。

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
}

1

こんにちは、このようなものはどうですか?インジェクター自体は非常にシンプルな実装であり、シングルトンであり、それにクラスも追加されています。もちろん、非常に簡単に拡張できます。もっと洗練されたものを探しているなら、このパッケージをチェックしてください: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

これはうまくいくはずです。

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._();するだろう。何を_(){}すべきか?これは冗長なようです。
ギュンターZöchbauer

申し訳ありませんが、これは提案であり、フォローアップの質問ではありません。_(){}はプライベートコンストラクタを作成しますよね?
Vilsad PP 2018

コンストラクタはクラス名で始まります。これは、戻り値の型が指定されていない、通常のプライベートインスタンスメソッドです。
ギュンターZöchbauer

1
反対投票して申し訳ありませんが、質が低く、既存の回答に加えて価値はありません。
ギュンターZöchbauer

2
このコードは質問に答えることがありますが、問題を解決する方法および/または理由に関する追加のコンテキストを提供すると、回答の長期的な価値が向上します。
カールリヒター

0

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));
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.