Flutterアプリ開発でFactoryコンストラクタを使う場面が最近増えてきてそもそものデザインパターンや設計について改めて深掘りたく記事にまとめ見ました。
この記事では、そもそものファクトリーデザインパターンについてと簡単なFactoryコンストラクタを使ってコードをシンプルにし、メンテナンスしやすくする方法を具体例とともに解説します。
初心者にもわかりやすく、シンプルな内容にまとめていますので、ぜひご参考ください。
ファクトリーデザインパターン定義
「Factory」は日本語で「工場」ですが、dartにおけるFactoryとはどういうなのか。
まずはファクトリーのデザインパターン定義では以下の通り。
ロジックをクライアントに公開することなくオブジェクトを作成し、共通のインターフェイスを用いて新しく作成されたオブジェクトを参照する。
we create objects without exposing the creation logic to the client and refer to newly created objects using a common interface.
これだけではよくわらかないと思うので実際にサンプル例を用いて理解を深めていきたいと思います。
一旦書いていることは2つで下記になります。
- ロジックをクライアントに公開することなくオブジェクトを作成
- 共通のインターフェイスを用いて新しく作成されたオブジェクトを参照

サンプル(Factory未使用)
犬クラスのサブクラスとしてラブラドールとドーベルマンクラスを作成。
class Dog{
String name;
Dog(this.name);
}
class Labrador extends Dog{
Labrador(String name):super(name);
}
class Doberman extends Dog{
Doberman(String name):super(name);
}犬に守ってもらいたい場合(isGuardianがtrue)ドーベルマンをそうじゃない場合(falseの場合)はラブラドールを呼んでいます。
void main(){
Dog dog;
String name='tommy';
bool isGuardian= false;
if(isGuardian == true) {
dog = Doberman(name);
} else{
dog = Labrador(name);
}
}この方法でも問題ありません。しかし、もし複数の場面でこの機能を実装する場合に都度if分を書いたりするのはイケてない。
そこでFactoryを使って直感的にかける。(そうです…)
サンプル(Factory使用)
犬クラスを継承したcreateDogというコンストラクタを使います。
引数としてnameという文字列とguardDogというbool型の引数を定義することですっきり直感的にかけました。
class Dog{
String name;
Dog(this.name);
factory Dog.createDog({required String name, bool guardDog=false}){
if(guardDog == true){
return Doberman(name);
}
else{
return Labrador(name);
}
}
}あとは、ドーベルマンを使用したい場合は以下のように使用します。
void main(){
Dog myGuardDog= Dog.createDog(name:’Rocky’,guardDog:true);
}これで定義であった下記2点が再現できたかなと思います。
- ロジックをクライアントに公開することなくオブジェクトを作成
- 共通のインターフェイスを用いて新しく作成されたオブジェクトを参照
クライアントとロジックの分離
ロジックをクライアントに公開することなくオブジェクトを作成
Factoryコンストラクタを使用することでクライアントとロジックを分離することができました。
ここでいうロジック(条件分岐)は、下記の部分で、クライアント(ここではmain())、オブジェクトはLabradorとDobermanのインスタンスになります。
if(guardDog == true){
return Doberman(name);
}
else{
return Labrador(name);
}ロジック(条件分岐)をクライアント(main())から切り離してクライアントに公開することなくオブジェクトを生成できます。createDogは裏でオブジェクトのどちらか(Labrador/Doberman)を生成します。
これでmain() には どのサブクラス(Labrador/Doberman)を作るかの条件が一切書かれていなく、main() はただ「Dog が欲しい」と言うだけでOKになりました。
void main(){
Dog myGuardDog= Dog.createDog(name:’Rocky’,guardDog:true);
}共通のインターフェイスを用いる
共通のインターフェイスを用いて新しく作成されたオブジェクトを参照
もう一つの上記のデザインパターン定義の部分についても見ていきましょう。
ここで言う共通のインターフェイスDog(型・親クラス)を用いて新しく生成されたオブジェクト(LabradorとDobermanのインスタンス)を参照してやりたいことを実現できています。
void main(){
Dog myGuardDog= Dog.createDog(name:’Rocky’,guardDog:true);
}繰り返しになりますが、createDogは内部的にオブジェクト(Doberman か Labrador)を参照し作成しています。これで共有のインターフェイスDogを用いてオブジェクト(Doberman か Labrador)を参照しています。
以上で、デザインパターンにあった2つの観点が達成できていることがわかると思います。
FactoryConstructorの動きをより理解したい方は以下もご参考ください。
最後に
Factoryを使用することで、コードのメンテナンスが容易になる場面や複数の条件分岐をシンプルに表現できる場面で効果的です。参考になったこちらの記事(英語ですが)も参考になったのでよかったらご覧ください。
ではまた。




コメント