Design Patterns 第3回 生成に関するパターン Abstract Factory
前回のリンク
生成に関するパターン
Abstract Factory
Abstract Factoryパターンを使うことで、それぞれの具体的なクラスを特定することなく共通のテーマを持った個々のファクトリをカプセル化できる。通常の使い方では、クライアント側のソフトウェアがAbstract Factoryの具体的な実装を作り、そしてそのテーマの一部の具体的なオブジェクトを生成するファクトリーのジェネリック(総称的)なインターフェースを使う。クライアント側では、その製品のジェネリックなインターフェースのみから使われるために、どの具体的なオブジェクトがそれぞれの内部のファクトリから得られるのか知らないか気にしないで済む。このパターンを使うことで、オブジェクトの生成がファクトリーインターフェースによって公開されているメソッドに実装されているように、オブジェクトの集合の実装の詳細を一般的な使い方から分離できるし、オブジェクトのコンポジションに任せることができる。たとえば(createLetter()やcreateResume()といった単一の製品を生成するインターフェースの抽象クラスのDocumentCreatorを考えてみる。そのシステムではFancyLetterやModernResumeのような対応するオブジェクトを生成するLetterやResumeといったいくつかの派生した具体的なDocumentoCreatorクラスのバージョンがあるかもしれない。それぞれの製品は、クライアントが把握しているLetterやResumeといった単純な抽象クラスから派生する。クライアント側のコードはDocumentCreatorの適切なインスタンスを得て、そのファクトリメソッドを呼ぶ。それらの結果オブジェクトは同一のDocumentCreatorの実装から生成され、共通のテーマ(すべて装飾的でモダンなテーマ)を共有する。クライアントは抽象クラスのLetterクラスやResumeクラスをどのように扱うのかだけを知っていればよい。
ファクトリーは、オブジェクトを構築するコードの具体的なクラスの場所だ。このパターンを適用する意図は、その使い方からオブジェクトの生成を分離することや具体的なクラスに依存せずに関係のある一連のオブジェクトを生成することにある。これによって、新しい派生型を、基底クラスで使われているコードを一切の変更なしに導入することができる。
このパターンを使うことによって、実行時でさえもそれらを使っているコードを変えることなしに、具体的な実装の交換ができるようになる。しかしながら、類似したデザインパターンと同様に、このパターンを適用することで、不要な複雑さを産み、初期化のコードの記述を増加させることになる。加えて、より高いレベルのの分離や抽象化はそのシステムのデバッグやメンテナンスの難易度を上げてしまうことになる。そのため、すべてのソフトウェアデザインで行われているように、トレードオフは慎重に考慮されるべきだ。
定義
Abstract Factory Patternの本質は「具体的なクラスを特定することなく一連の関連したオブジェクトを生成するインターフェースを提供する」ことにある。
使い方
ファクトリーは生成されるオブジェクトの実際の具体的な型を決定し、ここで、オブジェクトが実際に生成される(例えばC++ではnew演算子によって生成される)。けれども、ファクトリーは具体的なオブジェクトへの抽象クラスへのポインターを返すだけだ。
これによって、クライアント側が望んでいる抽象の型を生成するためのファクトリオブジェクトを問い合わせたり、そのオブジェクトへの抽象クラスへのポインターを返すことによって、クライアントコードからオブジェクトの生成を分離する。
ファクトリーが抽象クラスのポインターを返すことによって、クライアントコード(ファクトリーからオブジェクトをリクエストしているクライアントコード)はその単に生成されたオブジェクトの実際の具体的な型を知らないし、そのことによって負荷をかけない。けれども、具体的なオブジェクトの型、そしてそれゆえに具体的なファクトリーはAbstract Factoryにより知ることができる。たとえば、ファクトリーは設定ファイルからそれを読むかもしれない。クライアント側は型を特定する必要がない。なぜなら、既に設定ファイルで特定されているからだ。特にこれは以下のことを意味している。
- クライアントコードは具体的な型やどんなヘッダーファイルのインクルードやそれに関連したクラスの宣言を必要としているかを知らない。具体的な型のオブジェクトは本当にファクトリーによって生成されるが、クライアントコードはそのようなオブジェクトに抽象的なインターフェースを通してのみアクセスする。
- 新しい具体的な型を追加することは異なったファクトリーを使うクライアント側によって変更されることによってなされ、それというのは典型的に1つのファイルに1行変更するだけだ。異なったファクトリーはそれから異なった具体的な型のオブジェクトを生成するが、以前と同様にいまだに同一の抽象型のポインターを返すだけだ。そのようにして、変更からクライアントコードを隔絶する。(例えば具体的なクラスのヘッダーファイルをインクルードすることによって、そのようなコードの場所もまた新しい具体的な型のことを知っていることを確かめるのと同様に、)これは新しい型を初期化するコードを変更するよりもずっと簡単だ。新しい型を作った場合は普通はコードの新しいオブジェクトが生成されるすべての場所の変更を要求する。もしすべてのファクトリーのオブジェクトがSingletonオブジェクトに保存されていて、すべてのクライアントコードがオブジェクトの生成に適切なファクトリーにアクセスするのにSingletonを通すならば、ファクトリーを変更するのはシングルトンオブジェクトを変更するのと同じくらい簡単だ。
構造
クラスダイアグラム
出所: commons.wikimedia.org/wiki/File:Abstract_factory.svg
実装
実行結果
$ ./abstractfactory.exe Enter OS type (0: Windows, 1: MacOS X): 0 I'm a WinButton $ ./abstractfactory.exe Enter OS type (0: Windows, 1: MacOS X): 1 I'm an OSXButton