Factory Method パターン
Factory Method パターンは、オブジェクト生成のためのインターフェイスを定義しながら、生成されるオブジェクトの実際の型をサブクラスで決定できるようにするデザインパターンです。このパターンは「仮想コンストラクタ」とも呼ばれます。
目的と用途
Factory Method パターンの主な目的は以下の通りです:
- オブジェクト生成の処理をサブクラスに委譲すること
- クライアントコードとインスタンス化されるオブジェクトの実装を分離すること
- コードの拡張性を高め、新しい型のオブジェクトを追加しやすくすること
一般的な用途:
- フレームワークがさまざまな環境で動作する必要がある場合
- ライブラリが提供するクラスの実装詳細を隠蔽したい場合
- クラスの階層が複雑で、生成するインスタンスが多様な場合
- オブジェクトの生成とそのオブジェクトを使用するコードを分離したい場合
クラス図
図1: Factory Method パターンのクラス図
シーケンス図
図2: Factory Method パターンのシーケンス図
実装例
- 基本実装
- パラメータ化
- 静的メソッド
- 抽象工場
Factory Method パターンの最も基本的な実装です。抽象的な Creator クラスと具象的な ConcreteCreator クラスを使用します。
// 製品のインターフェース public interface Product { void operation(); } // 具象製品A public class ConcreteProductA implements Product { @Override public void operation() { System.out.println("ConcreteProductA の操作を実行"); } } // 具象製品B public class ConcreteProductB implements Product { @Override public void operation() { System.out.println("ConcreteProductB の操作を実行"); } } // 作成者の抽象クラス public abstract class Creator { // ファクトリーメソッド public abstract Product createProduct(); // 製品を使用する操作 public void someOperation() { // ファクトリーメソッドを呼び出してプロダクトを作成 Product product = createProduct(); // 作成された製品を使用 product.operation(); } } // 具象作成者A public class ConcreteCreatorA extends Creator { @Override public Product createProduct() { return new ConcreteProductA(); } } // 具象作成者B public class ConcreteCreatorB extends Creator { @Override public Product createProduct() { return new ConcreteProductB(); } } // クライアントコード public class Client { public static void main(String[] args) { Creator creatorA = new ConcreteCreatorA(); creatorA.someOperation(); // "ConcreteProductA の操作を実行" が出力される Creator creatorB = new ConcreteCreatorB(); creatorB.someOperation(); // "ConcreteProductB の操作を実行" が出力される } }
型引数を使用したパラメータ化ファクトリーメソッドの実装例です。
// 製品インターフェース public interface Product { void operation(); } // 具象製品A public class ConcreteProductA implements Product { @Override public void operation() { System.out.println("ConcreteProductA の操作を実行"); } } // 具象製品B public class ConcreteProductB implements Product { @Override public void operation() { System.out.println("ConcreteProductB の操作を実行"); } } // パラメータ化されたファクトリークラス public class ProductFactory { // パラメータに基づいて製品を作成するファクトリーメソッド public Product createProduct(String productType) { switch (productType) { case "A": return new ConcreteProductA(); case "B": return new ConcreteProductB(); default: throw new IllegalArgumentException("未知の製品タイプ: " + productType); } } } // クライアントコード public class Client { public static void main(String[] args) { ProductFactory factory = new ProductFactory(); Product productA = factory.createProduct("A"); productA.operation(); // "ConcreteProductA の操作を実行" が出力される Product productB = factory.createProduct("B"); productB.operation(); // "ConcreteProductB の操作を実行" が出力される } }
静的ファクトリーメソッドを使用した実装例です。
// 製品インターフェース public interface Product { void operation(); } // 具象製品A public class ConcreteProductA implements Product { // privateコンストラクタで直接インスタンス化を防止 private ConcreteProductA() {} @Override public void operation() { System.out.println("ConcreteProductA の操作を実行"); } // 静的ファクトリーメソッド public static ConcreteProductA createProductA() { return new ConcreteProductA(); } } // 具象製品B public class ConcreteProductB implements Product { // privateコンストラクタで直接インスタンス化を防止 private ConcreteProductB() {} @Override public void operation() { System.out.println("ConcreteProductB の操作を実行"); } // 静的ファクトリーメソッド public static ConcreteProductB createProductB() { return new ConcreteProductB(); } } // 製品ファクトリークラス public class ProductFactory { // 静的ファクトリーメソッド public static Product createProduct(String productType) { switch (productType) { case "A": return ConcreteProductA.createProductA(); case "B": return ConcreteProductB.createProductB(); default: throw new IllegalArgumentException("未知の製品タイプ: " + productType); } } } // クライアントコード public class Client { public static void main(String[] args) { // 静的ファクトリーメソッドを使用 Product productA = ProductFactory.createProduct("A"); productA.operation(); Product productB = ProductFactory.createProduct("B"); productB.operation(); } }
抽象工場パターンとの組み合わせ例です。
// 製品インターフェース public interface Product { void operation(); } // 具象製品A public class ConcreteProductA implements Product { @Override public void operation() { System.out.println("ConcreteProductA の操作を実行"); } } // 具象製品B public class ConcreteProductB implements Product { @Override public void operation() { System.out.println("ConcreteProductB の操作を実行"); } } // 製品バリエーション: 製品1 public interface Product1 extends Product {} // 製品バリエーション: 製品2 public interface Product2 extends Product {} // 具象製品A1 public class ConcreteProductA1 implements Product1 { @Override public void operation() { System.out.println("ConcreteProductA1 の操作を実行"); } } // 具象製品B1 public class ConcreteProductB1 implements Product1 { @Override public void operation() { System.out.println("ConcreteProductB1 の操作を実行"); } } // 具象製品A2 public class ConcreteProductA2 implements Product2 { @Override public void operation() { System.out.println("ConcreteProductA2 の操作を実行"); } } // 具象製品B2 public class ConcreteProductB2 implements Product2 { @Override public void operation() { System.out.println("ConcreteProductB2 の操作を実行"); } } // 抽象ファクトリー public abstract class AbstractFactory { // ファクトリーメソッド1 public abstract Product1 createProduct1(); // ファクトリーメソッド2 public abstract Product2 createProduct2(); } // 具象ファクトリーA public class ConcreteFactoryA extends AbstractFactory { @Override public Product1 createProduct1() { return new ConcreteProductA1(); } @Override public Product2 createProduct2() { return new ConcreteProductA2(); } } // 具象ファクトリーB public class ConcreteFactoryB extends AbstractFactory { @Override public Product1 createProduct1() { return new ConcreteProductB1(); } @Override public Product2 createProduct2() { return new ConcreteProductB2(); } } // クライアントコード public class Client { public static void main(String[] args) { // ファクトリーAを使用 AbstractFactory factoryA = new ConcreteFactoryA(); Product1 productA1 = factoryA.createProduct1(); Product2 productA2 = factoryA.createProduct2(); productA1.operation(); // "ConcreteProductA1 の操作を実行" が出力される productA2.operation(); // "ConcreteProductA2 の操作を実行" が出力される // ファクトリーBを使用 AbstractFactory factoryB = new ConcreteFactoryB(); Product1 productB1 = factoryB.createProduct1(); Product2 productB2 = factoryB.createProduct2(); productB1.operation(); // "ConcreteProductB1 の操作を実行" が出力される productB2.operation(); // "ConcreteProductB2 の操作を実行" が出力される } }
実際の使用例
Javaの標準ライブラリやフレームワークには、Factory Methodパターンを使用している例が多くあります:
- java.util.Calendar.getInstance() - カレンダーのインスタンスを取得するファクトリーメソッド
- java.text.NumberFormat.getInstance() - 数値をフォーマットするためのインスタンスを取得
- java.util.ResourceBundle.getBundle() - リソースバンドルを取得するファクトリーメソッド
- javax.xml.parsers.DocumentBuilderFactory.newInstance() - XMLドキュメントビルダーを作成するファクトリー
メリットとデメリット
メリット
- オブジェクトの生成ロジックとビジネスロジックを分離できる
- 製品クラスを容易に追加・変更できる(OCP原則に従う)
- コードの再利用性と保守性が向上する
- コンストラクタよりも明示的な名前をつけられる
デメリット
- 製品が1種類だけの場合は過剰設計になる可能性がある
- サブクラスの数が増えるとコードが複雑になる
- 適切な命名を考えないと、理解しにくいコードになりうる
- 設計の初期段階での導入には注意が必要
Factory MethodとSimple Factoryの違い
Factory Methodパターンと混同されやすいSimple Factory(単純工場)パターンとの違いは以下の通りです:
Factory Method | Simple Factory |
---|---|
|
|
関連パターン
- Abstract Factory: Factory Methodを複数使用して実装されることが多いです。Factory Methodは単一の製品に対するもの、Abstract Factoryは関連する製品のファミリーを作成するためのものです。
- Template Method: Factory Methodはtemplate methodの特殊なケースと見なせます。
- Prototype: プロトタイプパターンはクラスに依存しないオブジェクト生成を可能にします。Factory Methodとは異なるアプローチです。
- Singleton: Factory Methodの実装でシングルトンパターンが使用されることがあります。