Dartのfactoryを理解する


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

## Basic `factory` usage

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

```dart: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`の部分が、今回の重要なポイントです。

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

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

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

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

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

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

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

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

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

```dart
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の実装例が以下になります。

```dart: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()`的なメソッドが不要になっているのが特徴になります。

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

```dart
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._()`で充分