【Dart】Factory Constructorをかんたんに理解する
freezedという便利なパッケージの中でfactory
という言葉が頻出していて、理解せずに先に進むことが難しそうだったので調べてまとめました。参考にあるSaravanan M
さんの記事がとても参考になりました。
ファクトリーデザインパターンの定義は次のとおりです。
ロジックをクライアントに公開することなくオブジェクトを作成し、共通のインターフェイスを用いて新しく作成されたオブジェクトを参照する。
(?)。言葉では分かりづらいのでコードで表現します。
適当なDrink class
を定義して、そのサブクラスに水(Water)とコーヒー(Coffee)クラスを定義します。
class Drink {
int ammount;
Drink(this.ammount);
}
class Water extends Drink {
Water(int ammount) : super(ammount);
}
class Coffee extends Drink {
Coffee(int ammount) : super(ammount);
}
ユーザーが休憩を望んでいる場合、そのお供としてコーヒーを提供し、そうでない場合は水を提供します。
void main() {
Drink drink;
int ammount = 250;
bool isBreak = true;
isBreak ? drink = Coffee(ammount) : drink = Water(ammount);
// Instance of 'Coffee'
}
これでも問題ありませんが、複数の箇所でこのロジックを使う場合を考えるとif-else
を多用することになり、現実的ではありません。
どうすればこれを簡単にできるでしょうか?その答えがFactory Constructor
です。
class Drink{
int ammount;
Drink(this.ammount);
factory Drink.createDrink({required int ammount, bool breakTime = false})
=> breakTime ? Coffee(ammount) : Water(ammount);
}
void main() {
Drink myBreakDrink = Drink.createDrink(ammount: 200, breakTime: true);
// Instance of 'Coffee'
Drink mySportDrink = Drink.createDrink(ammount: 500);
// Instance of 'Water'
}
ここで再びファクトリーデザインパターンの定義を見てみます。
オブジェクトの作成ロジックをクライアントに公開することなく、オブジェクトを作成する。
オブジェクト生成ロジックをクライアント(=main())に公開していません。ロジックは親クラスDrink class
で定義されていて、クライアントは必要なオブジェクトを取得するために必要なパラメータを指定してファクトリーのコンストラクタを呼び出す必要があります。
新たに生成されたオブジェクトは、共通のインターフェイスを使って参照する。
Coffee(or Water)のインスタンスを Drink 型で返しているのであって、Coffee(or Water)として返しているわけではありません。親クラスは両方のサブクラスと互換性があるため、ここでは共通のインターフェイスとなっています。
Named Constructor との違い
Named Constructor
とはその名の通り名前の付いたコンストラクタです。
Java や C++では引数が異なる限り、複数のコンストラクタを持つクラスを書くことができます(オーバーロード)。Dart にはそのような機能はなく、明示的に名前をつけたコンストラクタを定義する必要があります。
class Drink {
int ammount;
Drink(this.ammount);
// Named Constructor
Drink.fromJson(...){
...
}
// Factory Constructor
factory Drink.createDrink(...){
...
}
}
Named Constructor
の場合、役割がより明確になり読みやすくなります(fromJson()であれば Json からインスタンスを作成しているんだなと推測できる)。
両者の違いは以下の通りで、必要に応じて使い分ければよさげですね。
インスタンスのメンバーへのアクセス
-
Named Constructor
は任意のメンバ変数およびメソッドにアクセスできます。 -
Factory Constructor
は静的なので、このキーワードにアクセスすることはできません。
return の有無
-
Named Constructor
は通常のコンストラクタと同様に動作し、明示的にインスタンスを返す必要はありません。 -
Factory Constructor
は明示的にインスタンスを返す必要があります。
生成されるインスタンスの型
-
Named Constructor
は、実行するクラスのインスタンスしか生成できません。 -
Factory Constructor
は実行時にどのインスタンスを返すかを決めることができ、サブクラスのインスタンスも返すことができます。
Discussion