
ソフトウェアエンジニアリングにおける抽象ファクトリーパターンは、具体的なクラスを指定せずに共通のテーマを持つ個々のファクトリーのグループをカプセル化することで、具体的なクラスを強制することなく関連オブジェクトのファミリーを作成する方法を提供する設計パターンです。 [ 1 ]このパターンによれば、クライアントソフトウェアコンポーネントは抽象ファクトリーの具体的な実装を作成し、ファクトリーの汎用インターフェースを使用してファミリーの一部である具体的なオブジェクトを作成します。クライアントは、製品の汎用インターフェースのみを使用するため、これらの内部ファクトリーのそれぞれからどの具体的なオブジェクトを受け取るかを知りません。[ 1 ]このパターンは、オブジェクトのセットの実装の詳細を一般的な使用から分離し、オブジェクトの作成がファクトリーインターフェースで公開されているメソッドで実装されるため、オブジェクト構成に依存します。[ 2 ]
このパターンを使用することで、実行時であっても、コードを変更することなく、具体的な実装を相互に交換することが可能になります。ただし、同様のデザインパターンと同様に、このパターンを採用すると、コードの初期記述時に不必要な複雑さや余分な作業が発生する可能性があります。さらに、分離と抽象化のレベルが高くなると、デバッグや保守がより困難になるシステムになる可能性もあります。
抽象ファクトリーデザインパターンは、1994 年版のデザインパターンの本で説明されている 23 のパターンの 1 つです。次のような問題を解決するために使用できます。[ 3 ]
オブジェクトを必要とするクラス内で直接オブジェクトを作成する方法は柔軟性に欠けます。そうすることでクラスは特定のオブジェクトに固定され、クラス自体を変更せずに後からインスタンス化を変更することは不可能になります。また、他のオブジェクトが必要な場合にクラスの再利用性を損ない、実際のオブジェクトをモックオブジェクトに置き換えることができないため、クラスのテストも困難になります。
ファクトリーとは、コード内でオブジェクトが構築される具象クラスの場所のことです。このパターンの実装の目的は、オブジェクトの作成をその使用から分離し、具象クラスに依存しない関連オブジェクトのファミリーを作成することです。[ 2 ]これにより、基底クラスを使用するコードを変更することなく、新しい派生型を導入できます。
このパターンは、そのような問題を解決する方法を説明しています。
これにより、クラスはオブジェクトの作成方法に依存しなくなります。クラスはファクトリオブジェクトで構成することができ、そのファクトリオブジェクトを使用してオブジェクトを作成します。また、実行時にファクトリオブジェクトを交換することもできます。
デザインパターンでは、抽象ファクトリーパターンを「具体的なクラスを指定せずに、関連するまたは依存するオブジェクトのファミリーを作成するためのインターフェース」と説明しています。[ 4 ]
ファクトリは作成するオブジェクトの具体的な型を決定し、ここで実際にオブジェクトが作成されます。ただし、ファクトリは作成された具体的なオブジェクトへの参照(Javaでは、例えばnew演算子による)または抽象型のポインタのみを返します。
これにより、クライアントがファクトリオブジェクトに目的の抽象型のオブジェクトを作成し、そのオブジェクトへの抽象ポインタを返すように要求することで、クライアントコードはオブジェクトの作成から隔離されます。 [ 5 ]
DocumentCreator一例として、複数の製品(例:createLetter()および)を作成するためのインターフェースを提供する抽象ファクトリクラスが挙げられますcreateResume()。システムには、やなどDocumentCreatorの派生した具体的なクラスがいくつも存在し、それぞれが異なる実装を持ち、やなどの対応するオブジェクトを作成します。これらの製品はそれぞれ、クライアントが認識している、やなどの単純な抽象クラスから派生しています。クライアントコードは、の適切なインスタンスを取得し、そのファクトリメソッドを呼び出します。結果として得られるオブジェクトはすべて同じ実装から作成され、共通のテーマを共有します。クライアントは、抽象クラスまたはクラスの処理方法を知るだけでよく、具体的なファクトリによって作成された特定のバージョンを知る必要はありません。FancyDocumentCreatorModernDocumentCreatorcreateLetter()createResume()FancyLetterModernResumeLetterResumeDocumentCreatorDocumentCreatorLetterResume
ファクトリは抽象型への参照またはポインタのみを返すため、ファクトリからオブジェクトを要求したクライアントコードは、作成されたオブジェクトの実際の具体的な型を認識せず、またその型に縛られることもありません。しかし、抽象ファクトリは具体的なオブジェクト(したがって具体的なファクトリ)の型を知っています。たとえば、ファクトリは構成ファイルからオブジェクトの型を読み込むことができます。型は構成ファイルですでに指定されているため、クライアントは型を指定する必要はありません。具体的には、次のようになります。

上記のUMLクラス図では、オブジェクトとオブジェクトをClient必要とするクラスは、とクラスを直接インスタンス化しません。代わりに、はオブジェクトを作成するためのインターフェースを参照し、オブジェクトの作成方法(どの具象クラスがインスタンス化されるか)に依存しないようにします。クラスは、とクラスをインスタンス化することでインターフェースを実装します。ProductAProductBProductA1ProductB1ClientAbstractFactoryClientFactory1AbstractFactoryProductA1ProductB1
UMLシーケンス図は、実行 時の相互作用を示しています。オブジェクトはオブジェクトを呼び出し、オブジェクトはオブジェクトを作成して返します。その後、オブジェクトはを呼び出し、オブジェクトはオブジェクトを作成して返します。ClientcreateProductA()Factory1ProductA1ClientcreateProductB()Factory1ProductB1
1994年に『デザインパターン』で定義された抽象ファクトリーパターンの本来の構造は、抽象ファクトリーと作成される抽象プロダクトのための抽象クラスに基づいています。具体的なファクトリーとプロダクトは、継承を使用して抽象クラスを特殊化したクラスです。[ 4 ]
このパターンのより新しい構造は、抽象ファクトリと作成される抽象プロダクトを定義するインターフェースに基づいています。この設計では、継承を避けるために、主流のプログラミング言語のインターフェースまたはプロトコルのネイティブサポートを使用します。この場合、具体的なファクトリとプロダクトは、インターフェースを実装することによってインターフェースを実現するクラスです。[ 1 ]
このC++23実装は、本書に掲載されているC++98以前の実装に基づいています。
import std ;using std :: array ; using std :: shared_ptr ; using std :: unique_ptr ; using std :: vector ;class MapSite { public : enum class Direction : char { NORTH , SOUTH , EAST , WEST , };virtual void enter () = 0 ; virtual ~ MapSite () = default ; };class Room : public MapSite { private : int roomNumber ; shared_ptr < array < MapSite , 4 >> sides ; public : explicit Room ( int n = 0 ) : roomNumber { n } {}~ Room () =デフォルト;Room & setSide ( MapSite :: Direction d , MapSite & ms ) { sides [ std :: to_underlying ( d )] = std :: move ( ms ); std :: println ( "Room::setSide {} ms" , d ); return * this ; }virtual void enter () override = 0 ;Room ( const Room & ) = delete ; Room & operator = ( const Room & ) = delete ; };class Wall : public MapSite { public : explicit Wall ( int n = 0 ) : MapSite ( n ) {}~ Wall () =デフォルト;void enter () override { // ... } };class Door : public MapSite { private : shared_ptr < Room > room1 ; shared_ptr < Room > room2 ; public : explicit Door ( int n = 0 , shared_ptr < Room > r1 = nullptr , shared_ptr < Room > r2 = nullptr ) : MapSite ( n ), room1 { std :: move ( r1 )}, room2 { std :: move ( r2 )} {}~ Door () =デフォルト;void enter () override { // ... }Door ( const Door & ) = delete ; Door & operator = ( const Door & ) = delete ; };class Maze { private : vector < shared_ptr < Room >> rooms ; public : Maze () = default ; ~ Maze () = default ;Maze & addRoom ( shared_ptr < Room > r ) { std :: println ( "Maze::addRoom {}" , reinterpret_cast < void *> ( r . get ())); rooms . push_back ( std :: move ( r )); return * this ; } shared_ptr < Room > roomNo ( int n ) const { for ( const Room & r : rooms ) { // 実際のルックアップロジックはここに記述... } return nullptr ; } };class MazeFactory { public : MazeFactory () = default ;virtual ~ MazeFactory () = default ;[[ nodiscard ]] unique_ptr < Maze > makeMaze () const { return std :: make_unique < Maze > (); }[[ nodiscard ]] shared_ptr < Wall > makeWall () const { return std :: make_shared < Wall > (); }[[ nodiscard ]] shared_ptr < Room > makeRoom ( int n ) const { return std :: make_shared < Room > ( new Room ( n )); } [[ nodiscard ]] shared_ptr < Door > makeDoor ( shared_ptr < Room > r1 , shared_ptr < Room > r2 ) const { return std :: make_shared < Door > ( std :: move ( r1 ), std :: move ( r2 )); } };// createMaze に、部屋、壁、ドアを作成するために使用するオブジェクトをパラメータとして渡すと、別のパラメータを渡すことで、部屋、壁、ドアのクラスを変更できます。これは、抽象ファクトリー (99) パターンの例です。class MazeGame { public : Maze () = default ; ~ Maze () = default ;[[ nodiscard ]] unique_ptr < Maze > createMaze ( MazeFactory & factory ) { unique_ptr < Maze > maze = factory . makeMaze (); shared_ptr < Room > r1 = factory . makeRoom ( 1 ); shared_ptr < Room > r2 = factory . makeRoom ( 2 ); shared_ptr < Door > door = factory . makeDoor ( r1 , r2 ); maze -> addRoom ( r1 ) . addRoom ( r2 ) . setSide ( MapSite :: Direction :: NORTH , factory . makeWall ()) . setSide ( MapSite :: Direction :: EAST , door ) . setSide ( MapSite :: Direction :: SOUTH , factory . makeWall ()) . setSide ( MapSite :: Direction :: WEST 、factory .makeWall ( )) 。setSide ( MapSite :: Direction :: NORTH 、factory .makeWall ( )) 。setSide ( MapSite :: Direction :: EAST 、factory .makeWall ( )) 。setSide ( MapSite :: Direction :: SOUTH 、factory .makeWall ( )) 。setSide ( MapSite ::方向:: WEST ,ドア);戻る迷路。} };int main ( int argc , char * argv [ ]) { MazeGame game ; unique_ptr <Maze> maze = game.createMaze ( MazeFactory ( ) ) ; }プログラムの出力は以下のとおりです。
Maze::addRoom 0x1317ed0 Maze::addRoom 0x1317ef0 Room::setSide 0 0x1318340 Room::setSide 2 0x1317f10 Room::setSide 1 0x1318360 Room::setSide 3 0x1318380 Room::setSide 0 0x13183a0ルーム::セットサイド 2 0x13183c0ルーム::セットサイド 1 0x13183e0ルーム::セットサイド 3 0x1317f10生成: Abstract Factory: 目的: 具体的なクラスを指定せずに、関連または依存するオブジェクトのファミリーを作成するためのインターフェースを提供します。
{{cite web}}: CS1 maint: bot: 元の URL の状態が不明です (リンク)ファクトリーは、製品やその作成方法の変更からクライアントを隔離し、非常に異なる抽象インターフェースから派生したオブジェクト間でこの隔離を提供することができます。
{{cite web}}: CS1 maint: bot: 元の URL の状態が不明です (リンク)