LoginSignup
38
20

More than 5 years have passed since last update.

Dartのfactoryを理解する

Posted at

Dartでは複数の方法でConstructorを定義する事ができますが、今回はFactoryによるConstructorの定義を理解したいと思います。

Basic factory usage

まず、factoryのコンストラクタを定義してあるFactoryExampleクラスを用意してみました。

FactoryExample.dart
class FactoryExample {
  static bool flag = true;
  String name;

  //Public factory constructor
  factory FactoryExample(){
    return flag ? new FactoryExample._('TypeA') : new FactoryExample._('TypeB');
  }

  //Private Constructor
  FactoryExample._(this.name);

  void printWhoAmI(){
    //Print `name` and identity hash code of self
    print("${this.name} [${identityHashCode(this)}]");
  }
}

以下のfactoryの部分が、今回の重要なポイントです。

//Public factory constructor
factory FactoryExample(){
  return flag ? new FactoryExample._('TypeA') : new FactoryExample._('TypeB');
}

staticなメンバ変数であるflagの値を元に、TypeATypeBというnameFactoryExampleのインスタンスを生成するようなコンストラクタになっています。

このクラスを使う側は以下のようになります。

void main() {
  new FactoryExample().printWhoAmI(); //TypeA [844190246]
}

最初はflagtrueなのでTypeAのインスタンスが生成されました。

次に、複数回生成してみます。

void main() {
  new FactoryExample()printWhoAmI(); //TypeA [243594479]
  new FactoryExample()printWhoAmI(); //TypeA [81303770]
}

IdentityHashCodeが異なることから、2つのTypeAのインスタンスが生成された事がわかります。

それでは、flagを書き換えてみるとどうでしょうか。

main() {
  new FactoryExample().printWhoAmI(); //TypeA [844190246]
  new FactoryExample().printWhoAmI(); //TypeA [783724485]
  FactoryExample.flag = false;
  new FactoryExample().printWhoAmI(); //TypeB [124951806]
  new FactoryExample().printWhoAmI(); //TypeB [1037459030]
}

flagを書き換え後はTypeBが生成されており、また、それぞれのインスタンスが別々のものである事が分かります。

当初私はfactoryをSingleton生成用の機能だと思ってたのですが、factoryそれ自身はFactoryPatternの実装を簡易にするための機能である事がわかります。

factory for singleton

それではシングルトンを実装するにはどうしたら良いのでしょうか。
先ほどと同じようにSingletonの実装例が以下になります。

SingletonExample.dart
class SingletonExample {
  static SingletonExample _singletonInstance;
  String name;

  //Public factory constructor
  factory SingletonExample(){
    if (_singletonInstance == null) {
      _singletonInstance = new SingletonExample._('SingletonA');
    }
    return _singletonInstance;
  }

  //Private Constructor
  SingletonExample._(this.name);

  void printWhoAmI() {
    print("${this.name} [${identityHashCode(this)}]");
  }
}

static変数の_singletonInstanceがあり、factoryコンストラクタ内において、nullチェックとインスタンスの生成を行っています。
特筆すべき事もないシングルトンの実装方法になります。

ただ、factoryを使う事で、getInstance()的なメソッドが不要になっているのが特徴になります。

では実際に使うとどうなるかが以下になります。

main() {
  new SingletonExample().printWhoAmI(); //SingletonA [973898827]
  new SingletonExample().printWhoAmI(); //SingletonA [973898827]
  new SingletonExample().name = 'SingletonA_Modified';
  new SingletonExample().printWhoAmI(); //SingletonA_Modified [973898827]
}

newしても毎回同じIdentityHashCodeが返ってきていて、4行目でnameを差し換えてますが、それ以降においてもnameの変更が反映され続けてる事からも、Singletonを実装できている事が確認できます。

ただ、new Hoge()がシングルトンである事にあまり慣れてない今の自分としては、(かつ、今後も外部パッケージを利用するような場合では見分けが付かないであろう事からも、)当然の帰結としてSingletonパターンの適用は慎重にする必要がある事を、強く求められている気がしちゃいます。

まとめ

  • factory Constructorを使う事で簡単にfactoryが作れる
  • factory Constructorを使うと簡単にSingletonが作れる
  • Singletonなのかそうじゃないのか、ぱっと見で見分けがつかなくなる
  • Singletonの適用は慎重になるべき
  • private ConstructorはミニマムだとClassName._()で充分
38
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
20