Dartでは複数の方法でConstructorを定義する事ができますが、今回はFactoryによるConstructorの定義を理解したいと思います。
Basic factory
usage
まず、factoryのコンストラクタを定義してあるFactoryExample
クラスを用意してみました。
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
の値を元に、TypeA
かTypeB
というname
のFactoryExample
のインスタンスを生成するようなコンストラクタになっています。
このクラスを使う側は以下のようになります。
void main() {
new FactoryExample().printWhoAmI(); //TypeA [844190246]
}
最初はflag
がtrue
なので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の実装例が以下になります。
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._()
で充分