Abstract Factory パターン
Abstract Factory(抽象ファクトリー)パターンは、関連するオブジェクトのファミリーを、具体的なクラスを指定せずに生成するためのインターフェースを提供するデザインパターンです。これにより、クライアントコードは特定の実装に依存せず、関連するオブジェクト群を一貫して作成できます。
目的と用途
Abstract Factory パターンの主な目的は以下の通りです:
- 互いに関連する一連のオブジェクト(製品ファミリー)を作成するインターフェースを提供する
- クライアントコードが具体的なクラスに依存せずにオブジェクトを生成できるようにする
- 関連する製品のファミリーが一貫して使用されることを保証する
- 具体的な実装を抽象化し、新しい製品ファミリーの追加を容易にする
一般的な用途:
- クロスプラットフォームのUI要素を作成する(Windowsスタイル、macOSスタイルなど)
- 異なるデータベース向けのコンポーネント(接続、クエリビルダー、トランザクションなど)を作成する
- 異なる環境やテーマに対応するオブジェクト群を生成する
- テスト環境と本番環境で異なる実装を切り替える
クラス図
図1: Abstract Factory パターンのクラス図
シーケンス図
図2: Abstract Factory パターンのシーケンス図
実装例
- 基本実装
- GUIコンポーネント例
- データベース接続例
Abstract Factory パターンの基本的な実装例です。製品ファミリーとそれを生成するファクトリーの関係を示しています。
GUIコンポーネントを表現する Abstract Factory パターンの実装例です。異なるOSスタイル(WindowsとmacOS)のUIコンポーネントを生成します。
データベース接続を表現する Abstract Factory パターンの実装例です。異なるデータベース(MySQLとPostgreSQL)用のコンポーネントを生成します。
実際の使用例
Javaの標準ライブラリやフレームワークには、Abstract Factory パターンを使用している例がいくつかあります:
- javax.xml.parsers.DocumentBuilderFactory - XMLドキュメントを解析するためのDocumentBuilder実装を作成します。
- javax.xml.transform.TransformerFactory - XMLドキュメントを変換するTransformer実装を生成します。
- javax.sql.DataSource - データベース接続を抽象化し、具体的なデータベースに依存しないコードを書けるようにします。
- java.util.Calendar - getInstance()メソッドは、ロケールや言語に応じた適切なCalendarインスタンスを返します。
- javax.enterprise.inject.spi.CDI - Java EEのコンテキストと依存性注入における実装を提供します。
メリットとデメリット
メリット
- 関連する製品オブジェクトが常に互換性を持つことを保証する
- クライアントコードと具体的な製品クラスの間の結合を減らす
- 単一責任の原則を満たす(製品の作成ロジックを分離)
- オープン/クローズドの原則を満たす(新しい種類の製品を追加しやすい)
- 製品ファミリー全体を一度に切り替え可能
デメリット
- 新しい種類の製品を追加するのが難しい(すべてのファクトリーを変更する必要がある)
- 製品ファミリーが多くなると、コードが複雑になる
- 抽象化の層が増えるため、コードが理解しにくくなる可能性がある
- 単一の製品のみを作成する場合には過剰な設計になる場合がある
関連パターン
- Factory Method: Abstract Factoryは通常、内部で複数のFactory Methodを使用して実装されます。Factory Methodは単一の製品の作成に焦点を当て、Abstract Factoryは製品ファミリー全体の作成に焦点を当てます。
- Singleton: Abstract Factoryの実装はしばしばSingletonとして実装されます。これは、アプリケーション全体で一貫した製品ファミリーを使用する必要があるためです。
- Prototype: Abstract Factoryは新しいオブジェクトを作成する代わりに、事前に作成されたオブジェクトのプロトタイプをクローンすることで実装できます。
- Builder: Abstract Factoryは複雑な製品を一度に作成するのに対し、Builderは複雑なオブジェクトをステップバイステップで構築します。
- Bridge: Bridgeと組み合わせて使うと、抽象化された実装の様々なバリエーションを作成できます。
実装時の注意点
- 製品の一貫性: ファクトリーで生成される製品間の一貫性を保証する必要があります。例えば、WindowsスタイルのボタンとmacOSスタイルのチェックボックスは混在させないようにします。
- 製品インターフェースの設計: 製品インターフェースは十分に抽象化されていて、すべての具象実装に適用できるようにする必要があります。
- ファクトリーの選択メカニズム: どのファクトリーを使用するかを決定するメカニズムを設計する必要があります(例:設定ファイル、環境変数、実行時の条件など)。
- 製品数の増加による複雑化: 製品の種類が多い場合、すべてのファクトリーで対応するメソッドを追加する必要があり、メンテナンスが難しくなることがあります。
- 製品ファミリーの拡張: 既存のファクトリーに新しい製品を追加するのは難しいため、最初の設計段階で必要な製品をすべて識別することが重要です。