Factoryパターン
Creational Design Pattern の一つで、クライアントに対してインスタンスの生成を隠蔽するためのパターン。
具象クラスのインスタンス化を隠蔽またはカプセル化することで、クライアントは直接クラスをnew
演算子によってインスタンス化するのではなく、ファクトリーに生成を依頼するようになる。
これにより、クライアントコードから具象クラスがなくなり、「変更に対しては閉じられているが、拡張に対しては開かれている」状態となる。
Factoryパターンには以下の3種類が存在する。
- Simple Factory
- Factory Method
- Abstract Factory
Simple Factory
厳密にはGoF(Gang of Four)によって定義されるデザインパターンではないが、クライアントから具象クラスを分離させるための一つのスタイルである。
インスタンスを返却するメソッドがコンストラクタの役割を果たすことにより、具象クラスのインスタンス化をカプセル化する。
どの具象クラスをインスタンス化するのかを、クライアントがメソッドの引数(パラメータ)によって決定する。
クラスが Factory (インスタンス生成)の役割を果たす。
生成する具象クラスを、クライアントが選択する
クラスが Factory
Simple Factoryの例
生成するオブジェクトは、現時点でJapaneseFood
、FrenchFood
、ChineseFood
の3種類が存在し、それぞれFood
インターフェースを実装しているものとする。
これらのオブジェクトはResutaurant
クラス(Food
のクライアントに相当)で利用されていて、注文に応じて3種類の中から1つを選択してcook()
が実行される。
// Factory で生成されるクラスのインターフェース
public interface Food {
public void cut();
public void addSalt();
public void addSugar();
}
// Factory で生成される具象クラス①
public class JapaneseFood implements Food {
@Override
public void cut() {
System.out.println("食材を千切りします。");
}
@Override
public void addSalt() {
System.out.println("塩を5g加えます。");
}
@Override
public void addSugar() {
System.out.println("砂糖を5g加えます。");
}
}
// Factory で生成される具象クラス②
public class FrenchFood implements Food {
@Override
public void cut() {
System.out.println("食材を乱切りします。");
}
@Override
public void addSalt() {
System.out.println("塩を1g加えます。");
}
@Override
public void addSugar() {
System.out.println("砂糖を1g加えます。");
}
}
// Factory で生成される具象クラス③
public class ChineseFood implements Food {
@Override
public void cut() {
System.out.println("食材を輪切りします。");
}
@Override
public void addSalt() {
System.out.println("塩を10g加えます。");
}
@Override
public void addSugar() {
System.out.println("砂糖を10g加えます。");
}
}
// Factory を利用する、クライアントに当たるクラス
public class Restaurant {
FoodFactory factory;
public Restaurant(FoodFactory factory) {
this.factory = factory;
}
public void cook() {
// new が使用されていない
// クライアントが具象クラスを選択している
Food food = factory.createFood("japanese");
food.cut();
food.addSalt();
food.addSugar();
}
}
// Factory クラス
// 具象クラスのインスタンス化がここに集約・カプセル化、隠蔽されている
public class FoodFactory {
// static にしてもOK(ただし static の場合、継承による機能拡張ができない)
public Food createFood(String type) {
if (type.equals("japanese")) {
return new JapaneseFood();
} else if (type.equals("chinese")) {
return new ChineseFood();
} else if (type.equals("french")) {
return new FrenchFood();
} else {
throw new IllegalArgumentException();
}
}
}
public class Main {
public static void main(String[] args) {
Restaurant restaurant = new Restaurant(new FoodFactory());
restaurant.cook();
// >>食材を千切りします。
// >>塩を5g加えます。
// >>砂糖を5g加えます。
}
}
Simple Factory を利用したことで、クライアントのプログラムコードから具象クラスが消え、クライアントが具象クラスにではなく、インターフェースに依存するようになった(具象クラスに依存するよりも、抽象クラスに依存するプログラミングが良いとされる)。
Factory Method
インスタンス生成するメソッド( Factory )をスーパークラスの抽象メソッドなどで定義し、継承したサブクラスが実際にどの具象クラスをインスタンス化するのかを実装する。ただし、どの Factory を利用するかの選択はクライアントが行う。
生成する具象クラスを、サブクラスが選択する
使用する Factory は、クライアントが選択する
メソッドが Factory(一つのメソッドが一つのオブジェクトを生成する)
継承を利用する
メソッドが Factory (インスタンス生成)の役割を果たす。
Factory Method の利用例
Food
インターフェースを実装した、以下の6つの具象クラスが存在するとする。
CasualJapaneseFood
CasualChineseFood
CasualFrenchFood
LuxuriousJapaneseFood
LuxuriousChineseFood
LuxuriousFrenchFood
これらの具象クラスを生成するメソッドはcreateFood()
だが、japanese
が指定された時にCasualJapaneseFood
を生成する Factory と、LuxuriousJapaneseFood
を生成する Factory が必要になる。
そこで、Factory (インスタンス生成を行うメソッド)をスーパークラスの抽象メソッド(createFood()
)として用意して、サブクラスCasualRestaurant
とサブクラスLuxuriousRestaurant
で実装する。
クライアントはまず、以下の2つの選択を行う。
-
japanese
orchinese
orfrench
(この選択は必須ではない) -
CasualRestaurant
orLuxuriousRestaurant
(Factoryの選択)
選択された Restaurant は、具象クラス6つの中から一つを選択して、インスタンスの生成を行う。例えば、japanese
、CasualRestaurant
が選択された場合、CasualJapaneseFood
の生成を行う。
ここで重要なのは、クライアントは Simple Factory と同様に、具象クラスの選択をしていないということ。あくまでも具象クラスの選択はサブクラスが行なっていて、クライアントは Factory の選択をしている。
public interface Food {
public void cut();
public void addSalt();
public void addSugar();
}
// 具象クラス①
public class CasualJapaneseFood implements Food {
@Override
public void cut() { System.out.println("普通の包丁で、食材を千切りします。"); }
@Override
public void addSalt() { System.out.println("普通の塩を、5g加えます。"); }
@Override
public void addSugar() { System.out.println("普通の砂糖を、5g加えます。"); }
}
// 具象クラス②
public class CasualChineseFood implements Food {
@Override
public void cut() { System.out.println("普通の包丁で、食材を千切りします。"); }
@Override
public void addSalt() { System.out.println("普通の塩を、5g加えます。"); }
@Override
public void addSugar() { System.out.println("普通の砂糖を、5g加えます。"); }
}
// 具象クラス③
public class CasualFrenchFood implements Food {
@Override
public void cut() { System.out.println("普通の包丁で、食材を乱切りします。"); }
@Override
public void addSalt() { System.out.println("普通の塩を、1g加えます。"); }
@Override
public void addSugar() { System.out.println("普通の砂糖を、1g加えます。"); }
}
// 具象クラス④
public class LuxuriousJapaneseFood implements Food {
@Override
public void cut() { System.out.println("高級な包丁で、食材を千切りします。"); }
@Override
public void addSalt() { System.out.println("高級な塩を、5g加えます。"); }
@Override
public void addSugar() { System.out.println("高級な砂糖を、5g加えます。"); }
}
// 具象クラス⑤
public class LuxuriousChineseFood implements Food {
@Override
public void cut() { System.out.println("高級な包丁で、食材を輪切りします。"); }
@Override
public void addSalt() { System.out.println("高級な塩を、10g加えます。"); }
@Override
public void addSugar() { System.out.println("高級な砂糖を、10g加えます。"); }
}
// 具象クラス⑥
public class LuxuriousFrenchFood implements Food {
@Override
public void cut() { System.out.println("高級な包丁で、食材を乱切りします。"); }
@Override
public void addSalt() { System.out.println("高級な塩を、1g加えます。"); }
@Override
public void addSugar() { System.out.println("高級な砂糖を、1g加えます。"); }
}
// スーパークラス
public abstract class Restaurant {
public void cook(String type) {
Food food = createFood(type);
food.cut();
food.addSalt();
food.addSugar();
}
// ⭐️ Factory Method (インスタンスを生成するメソッド) ⭐️
// ただし、抽象メソッドのため、実装はサブクラスが行う(デフォルトメソッドとしてもOK)
// Restaurant は実際にどの具象クラスが生成されるかを知らない。Restaurant を利用するクライアントも知らない。
protected abstract Food createFood(String type);
}
// サブクラス①
public class CasualRestaurant extends Restaurant {
// サブクラスが生成する具象クラスを選択する
@Override
protected Food createFood(String type) {
if (type.equals("japanese")) {
return new CasualJapaneseFood(); // 具象クラス①
} else if (type.equals("chinese")) {
return new CasualChineseFood(); // 具象クラス②
} else if (type.equals("french")) {
return new CasualFrenchFood(); // 具象クラス③
} else {
throw new IllegalArgumentException();
}
}
}
// サブクラス②
public class LuxuriousRestaurant extends Restaurant {
// サブクラスが生成する具象クラスを選択する
@Override
protected Food createFood(String type) {
if (type.equals("japanese")) {
return new LuxuriousJapaneseFood(); // 具象クラス④
} else if (type.equals("chinese")) {
return new LuxuriousChineseFood(); // 具象クラス⑤
} else if (type.equals("french")) {
return new LuxuriousFrenchFood(); // 具象クラス⑥
} else {
throw new IllegalArgumentException();
}
}
}
// クライアント
public class Main {
public static void main(String[] args){
// クライアントは Factory の選択をするが、どの具象クラスが生成されるかを知らない
Restaurant restaurant = new CasualRestaurant();
restaurant.cook("japanese");
// >>普通の包丁で、食材を千切りします。
// >>普通の塩を、5g加えます。
// >>普通の砂糖を、5g加えます。
}
}
Abstract Factory
複数の関連するオブジェクト(オブジェクト群、Family)を、コンポジションによって保持された Factory がまとめて生成をする。
Factory (インスタンス生成のメソッドが集約されたクラス)は、インターフェースで定義する。複数の Factory が存在し、インターフェースを実装したそれぞれの Factory が関連するオブジェクトをまとめて生成する。
Abstract Factory の内部では、Factory Method が利用されている。
Factory of Factoriesとも呼ばれることもある。
使用する Factory は、クライアントが選択する
メソッドが Factory(一つのメソッドが複数のオブジェクトを生成する)
コンポジションを利用する
Abstract Factory の利用例
Sugar
とSalt
から構成されるオブジェクト群が存在するとして、
-
CasualSugar
、CasualSalt
というオブジェクト群 -
LuxuriousSugar
、LuxuriousSalt
というオブジェクト群
の2種類があるものとする。
Casual
と名前がつくオブジェクト群と、Luxurious
と名前がつくオブジェクト群は、それぞれ別でまとめて生成したい。
このような場合に、メソッド prepare()
をCondimentShop
クラスに用意して、保持(コンポジション)している Factory クラスを利用して、まとめてオブジェクト群の生成が行われる。
// オブジェクト群の構成要素①
public interface Sugar {
public void description();
}
// オブジェクト群の構成要素②
public interface Salt {
public void description();
}
// オブジェクト群の構成要素①
public class CasualSugar implements Sugar {
public void description() {
System.out.println("普通の砂糖です。");
}
}
// オブジェクト群の構成要素②
public class CasualSalt implements Salt {
@Override
public void description() {
System.out.println("普通の塩です。");
}
}
// オブジェクト群の構成要素①
public class LuxuriousSugar implements Sugar {
public void description() {
System.out.println("高級な砂糖です。");
}
}
// オブジェクト群の構成要素②
public class LuxuriousSalt implements Salt {
public void description() {
System.out.println("高級な塩です。");
}
}
// Factory インターフェース
public interface CondimentFactory {
public Salt createSalt();
public Sugar createSugar();
}
// オブジェクト群①を生成するための Factory
public class CasualCondimentFactory implements CondimentFactory {
@Override
public Salt createSalt() {
return new CasualSalt();
}
@Override
public Sugar createSugar() {
return new CasualSugar();
}
}
// オブジェクト群②を生成するための Factory
public class LuxuriousCondimentFactory implements CondimentFactory {
@Override
public Salt createSalt() {
return new LuxuriousSalt();
}
@Override
public Sugar createSugar() {
return new LuxuriousSugar();
}
}
// Factory を保持するクラス
public class CondimentShop {
CondimentFactory factory; // Factory が保持されている
Sugar sugar;
Salt salt;
public CondimentShop(CondimentFactory factory) {
this.factory = factory;
}
// オブジェクト群を生成するためのメソッド
public void prepare() {
sugar = factory.createSugar();
salt = factory.createSalt();
}
public void description() {
sugar.description();
salt.description();
}
}
// クライアント
public class Main {
public static void main(String[] args) {
// クライアントは Factory を選択するが、Salt、Sugarの具象クラスについては何も知らない(依存関係がない)
CondimentFactory casualFactory = new CasualCondimentFactory();
CondimentShop casualShop = new CondimentShop(casualFactory);
casualShop.prepare(); // 具象クラスのオブジェクト群を生成するメソッド(new が登場しない = 具象クラスに依存しない)
casualShop.description();
// >>普通の砂糖です。
// >>普通の塩です。
// クライアントは Factory を選択するが、Salt、Sugarの具象クラスについては何も知らない(依存関係がない)
CondimentFactory luxuriousFactory = new LuxuriousCondimentFactory();
CondimentShop luxuriousShop = new CondimentShop(luxuriousFactory);
luxuriousShop.prepare(); // 具象クラスのオブジェクト群を生成するメソッド(new が登場しない = 具象クラスに依存しない)
luxuriousShop.description();
// >>高級な砂糖です。
// >>高級な塩です。
}
}