オブジェクト指向プログラミングにおいて、ファクトリメソッドパターンは、オブジェクトの正確なクラスを指定せずにオブジェクトを作成するという問題に対処するためにファクトリメソッドを使用する設計パターンです。これは、コンストラクターを呼び出すのではなく、ファクトリメソッドを呼び出してオブジェクトを作成することによって実現されます。ファクトリメソッドは、インターフェースで指定してサブクラスで実装することも、基本クラスで実装してサブクラスでオーバーライドすることもできます。これは、 『デザインパターン』 (「Gang of Four」または単に「GoF」と呼ばれることが多い)という書籍に記載されている23の古典的な設計パターンの1つであり、作成パターンとしてサブカテゴリ化されています。[ 1 ]
概要
ファクトリメソッド設計パターンは次のような問題を解決します。
- オブジェクトのサブクラスは、その後の個別の実装をどのように再定義できるでしょうか。このパターンでは、オブジェクトの作成をサブクラスのファクトリ メソッドに委ねるファクトリ メソッドをスーパークラス内に作成します。
- オブジェクトのインスタンス化をサブクラスに延期するにはどうすればよいでしょうか? コンストラクターを直接呼び出すのではなく、ファクトリー メソッドを呼び出してオブジェクトを作成します。
これにより、オブジェクトの作成方法を変更できるサブクラスの作成が可能になります (たとえば、インスタンス化するクラスを再定義するなど)。
意味
デザインパターン:再利用可能なオブジェクト指向ソフトウェアの要素によると、「オブジェクトを作成するためのインターフェースを定義し、サブクラスがどのクラスをインスタンス化するかを決定します。ファクトリーメソッドは、クラスのインスタンス化をサブクラスに委ねます。」[ 2 ]
オブジェクトの作成には、多くの場合、構成オブジェクトに含めるのが適切ではない複雑なプロセスが必要になります。オブジェクトの作成によって、コードの大幅な重複が発生したり、構成オブジェクトがアクセスできない情報が必要になったり、十分な抽象化レベルが提供されなかったり、構成オブジェクトの関心事に含まれなかったりする場合があります。ファクトリーメソッド設計パターンは、オブジェクトを作成するための別のメソッドを定義することでこれらの問題に対処します。サブクラスは、このメソッドをオーバーライドすることで、作成される製品の 派生型を指定できます。
ファクトリーメソッドパターンは継承に依存しており、オブジェクトの作成は、オブジェクトを作成するファクトリーメソッドを実装するサブクラスに委任されます。[ 3 ]このパターンは、インターフェース の実装にも依存します。
構造
UMLクラス図

上記のUMLクラス図では、オブジェクトCreatorを必要とするクラスは、クラスを直接Productインスタンス化しませんProduct1。代わりに、 はCreator別の を参照してfactoryMethod()製品オブジェクトを作成します。これにより、Creatorはインスタンス化された具体的なクラスとは独立しています。 のサブクラスは、Creatorどのクラスをインスタンス化するかを再定義できます。この例では、サブクラスはクラスをインスタンス化することでCreator1抽象 を実装しています。 factoryMethod()Product1
例
構造
迷路ゲームは、隣接する部屋とのみ接続された通常の部屋を使用するモードと、プレイヤーがランダムに移動できる魔法の部屋を使用するモードの 2 つのモードでプレイできます。

RoomMagicRoomは最終製品(または)の基底クラスですOrdinaryRoom。MazeGameそのような基底製品を生成するための抽象ファクトリーメソッドを宣言します。MagicRoomおよびは、OrdinaryRoom最終製品を実装する基底製品のサブクラスです。MagicMazeGameおよびは、最終製品を生成するファクトリーメソッドOrdinaryMazeGameを実装するサブクラスです。このように、ファクトリーメソッドは呼び出し元( )を具象クラスの実装から分離します。これにより、演算子が冗長になり、オープン・クローズ原則を遵守できるようになり、変更時の最終製品への柔軟性が向上します。 MazeGameMazeGamenew
実装例
このC++23の実装は、デザインパターンの本に記載されているC++98以前の実装に基づいています。[ 5 ]
stdをインポートします。std :: unique_ptrを使用します。enumクラスProductId : char { MINE , YOURS };// ファクトリメソッドが作成するオブジェクトのインターフェースを定義します。class Product { public : virtual void print () = 0 ; virtual ~ Product () = default ; };// Product インターフェイスを実装します。class ConcreteProductMINE : public Product { public : void print () { std :: println ( "this= { } print MINE" , this ); } };// Product インターフェイスを実装します。class ConcreteProductYOURS : public Product { public : void print () { std :: println ( "this={} print YOURS" , this ); } };// Product 型のオブジェクトを返すファクトリー メソッドを宣言します。class Creator { public : virtual unique_ptr < Product > create ( ProductId id ) { switch ( id ) { case ProductId :: MINE : return std :: make_unique < ConcreteProductMINE > ( ); case ProductId :: YOURS : return std :: make_unique < ConcreteProductYOURS > (); // 残りの製品について繰り返しますdefault : return nullptr ; } }仮想~ Creator () =デフォルト; };int main ( int argc , char * argv []) { unique_ptr < Creator > creator = std :: make_unique < Creator > (); unique_ptr < Product > product = creator -> create ( ProductId :: MINE ); product -> print ();product = creator -> create ( ProductId :: YOURS ); product -> print (); }プログラムの出力は次のようになります
this = 0x6e5e90印刷MINE this = 0x6e62c0印刷YOURS// 実際のオブジェクトの空の語彙public interface IPerson { string GetName (); }パブリッククラスVillager : IPerson {パブリック文字列 GetName () { return "村人" ; } }パブリッククラスCityPerson : IPerson {パブリック文字列GetName () {戻り値"City Person" ; } }パブリック列挙型PersonType {農村、都市}/// <summary> /// ファクトリーの実装 - オブジェクトの作成に使用されます。/// </summary> public class PersonFactory { public IPerson GetPerson ( PersonType type ) { switch ( type ) { case PersonType . Rural : return new Villager (); case PersonType . Urban : return new CityPerson (); default : throw new NotSupportedException (); } } }上記のコードは、 というインターフェースと、およびIPersonという2つの実装の作成を示しています。オブジェクトに渡された型に基づいて、元の具象オブジェクトがインターフェース として返されます。 VillagerCityPersonPersonFactoryIPerson
ファクトリーメソッドはPersonFactoryクラスへの追加機能です。インターフェースを介してクラスのオブジェクトを作成するだけでなく、サブクラスがどのクラスをインスタンス化するかを決定することもできます。
パブリックインターフェイスIProduct {文字列GetName (); bool SetPrice ( double price ); }パブリッククラスPhone : IProduct {プライベートdouble _price ;パブリック文字列GetName () { "Apple TouchPad"を返します; }public bool SetPrice ( double price ) { _price = price ; trueを返す; } }/* Factory とほぼ同じですが、作成されたメソッドで何かを行うための追加の公開のみです */ public abstract class ProductAbstractFactory { protected abstract IProduct MakeProduct ();public IProduct GetObject () // ファクトリーメソッドの実装。{ return this . MakeProduct (); } }public class PhoneConcreteFactory : ProductAbstractFactory { protected override IProduct MakeProduct () { IProduct product = new Phone (); // オブジェクトを受け取ったら何かするproduct . SetPrice ( 20.30 ); return product ; } }この例では、MakeProductが で使用されていますconcreteFactory。その結果、MakeProduct()からそれを取得するために が呼び出される可能性がありますIProduct。具体的なファクトリメソッドでオブジェクトが取得された後に、カスタムロジックが実行される場合があります。GetObjectはファクトリインターフェースで抽象化されます。
このJava の例は、 『Design Patterns』という書籍の例に似ています。
は、具体的なクラスを作成するサブクラスにオブジェクト の作成を委譲しMazeGameますが、そのRoom役割は委譲されません。通常のゲームモードでは、次のテンプレートメソッドを使用できます。Room
パブリック抽象クラスRoom { abstract void connect ( Room room ); }パブリッククラスMagicRoom はRoomを拡張します{ public void connect ( Room room ) {} }パブリッククラスOrdinaryRoomはRoomを拡張します{ public void connect ( Room room ) {} }パブリック抽象クラスMazeGame {プライベート最終リスト< Room > rooms = new ArrayList <> ();public MazeGame ( ) { Room room1 = makeRoom ( ); Room room2 = makeRoom () ; room1.connect ( room2 ) ; rooms.add ( room1 ) ; rooms.add ( room2 ) ; }抽象保護されたRoom makeRoom (); }コンストラクタは、共通ロジックを追加するテンプレートメソッドMazeGameです。これは、部屋の作成をカプセル化するファクトリーメソッドを参照し、サブクラスで他の部屋を使用できるようにします。魔法の部屋を含む他のゲームモードを実装するには、このメソッドをオーバーライドします。 makeRoom()makeRoom
パブリッククラスMagicMazeGame はMazeGameを拡張します{ @Override保護されたMagicRoom makeRoom () {新しいMagicRoom ()を返します; } }パブリッククラスOrdinaryMazeGameはMazeGameを拡張します{ @Override保護されたOrdinaryRoom makeRoom (){ return new OrdinaryRoom (); } }迷路ゲームordinaryGame =新しいOrdinaryMazeGame ();迷路ゲームmagicGame =新しいMagicMazeGame ();このPHPの例では、サブクラス化ではなくインターフェース実装を示しています(ただし、サブクラス化でも同じことが実現できます)。ファクトリーメソッドは、public(前述のJavaの例とは異なり)として定義し、クライアントコードから直接呼び出すこともできます。
/* 工場と車のインターフェース */インターフェースCarFactory {パブリック関数makeCar () : Car ; }インターフェースCar { public function getType () : string ; }/* ファクトリーと車の具体的な実装 */クラスSedanFactoryはCarFactoryを実装します{ public function makeCar () : Car { return new Sedan (); } }クラスSedanはCarを実装します{ public function getType () : string { return 'Sedan' ; } }/* クライアント */$factory = new SedanFactory (); $car = $factory -> makeCar (); $car -> getType ()を印刷します。この Python の例では、前の Java の例と同じものを採用しています。
abcからABCをインポート、抽象メソッドクラスMazeGame ( ABC ): def __init __ ( self ) - > None : self.rooms = [ ] self._prepare_rooms ( )def _prepare_rooms ( self ) - > None : room1 = self.make_room ( ) room2 = self.make_room ( )room1.connect ( room2 ) self.rooms.append ( room1 ) self.rooms.append ( room2 )def play ( self ) -> None : print ( f " { self . rooms [ 0 ] }を使用してプレイしています" )@abstractmethod def make_room ( self ): raise NotImplementedError ( "これを実装する必要があります!" )クラスMagicMazeGame ( MazeGame ): def make_room ( self ) -> "MagicRoom" : return MagicRoom ()クラスOrdinaryMazeGame ( MazeGame ): def make_room ( self ) -> "OrdinaryRoom" : return OrdinaryRoom ()クラスRoom ( ABC ): def __init__ ( self ) -> None : self . connected_rooms = []def connect ( self , room : " Room " ) - > None : self.connected_rooms.append ( room )class MagicRoom ( Room ): def __str__ ( self ) -> str : return "Magic room"class OrdinaryRoom ( Room ): def __str__ ( self ) -> str : return "Ordinary room"ordinaryGame = OrdinaryMazeGame ( ) ordinaryGame.play ( )magicGame = MagicMazeGame ( ) magicGame.play ( )用途
- ADO.NETでは、IDbCommand.CreateParameterは、ファクトリ メソッドを使用して並列クラス階層を接続する例です。
- Qtでは、QMainWindow::createPopupMenu ( Wayback Machineで 2015-07-19 にアーカイブ)は、フレームワークで宣言され、アプリケーション コードでオーバーライドできるファクトリー メソッドです。
- Javaでは、javax.xml.parsers.DocumentBuilderFactory や javax.xml.parsers.SAXParserFactory など、javax.xml.parsersパッケージでいくつかのファクトリが使用されます。
- HTML5 DOM APIでは、Document インターフェイスに、HTMLElement インターフェイスの特定の要素を作成するための createElement() ファクトリ メソッドが含まれています。
参照
- 非常に影響力のある本『デザインパターン』
- デザインパターン、一般的なデザインパターンの概要
- 抽象ファクトリーパターン、ファクトリーメソッドを使用して実装されることが多いパターン
- ビルダーパターン、もう一つの創造パターン
- テンプレートメソッドパターン(ファクトリーメソッドを呼び出す可能性がある)
- Joshua Blochの静的ファクトリーメソッドのアイデア。Blochは、デザインパターンにはこれと直接同等のものは存在しないと主張しています。
注記
- ^ガンマら1995年、107ページ。
- ^ガンマ, エリック;ヘルム, リチャード;ジョンソン, ラルフ;ブリシデス, ジョン(1995). 『デザインパターン:再利用可能なオブジェクト指向ソフトウェアの要素』 . アディソン・ウェズレー. ISBN 0-201-63361-2。
- ^フリーマン, エリック; ロブソン, エリザベス; シエラ, キャシー; ベイツ, バート (2004). ヘンドリクソン, マイク; ルーキデス, マイク (編). Head First Design Patterns: A Brain-Friendly Guide (ペーパーバック) . 第1巻 (第1版). オライリーメディア. 162ページ. ISBN 978-0-596-00712-6. 2012年9月12日閲覧。
- ^ 「ファクトリーメソッド設計パターン - 構造とコラボレーション」w3sDesign.com . 2017年8月12日閲覧。
- ^ガンマら1995年、122ページ。
参考文献
- マーティン・ファウラー、ケント・ベック、ジョン・ブラント、ウィリアム・オプダイク、ドン・ロバーツ(1999年6月)。『リファクタリング:既存コードの設計改善』Addison-Wesley。ISBN 0-201-48567-2。
- コックス、ブラッド・J. (1986).オブジェクト指向プログラミング:進化的アプローチ. アディソン・ウェズレー. ISBN 978-0-201-10393-9。
- Cohen, Tal; Gil, Joseph (2007). 「ファクトリーによるより良い建設」(PDF) . Journal of Object Technology . 6 (6). Bertrand Meyer : 103. doi : 10.5381/jot.2007.6.6.a3 . 2007年3月12日閲覧.
外部リンク
- ファクトリーデザインパターンアーカイブ2018-01-10 at the Wayback Machine Javaでの実装
- UMLとLePUS3 (設計記述言語)におけるファクトリーメソッド
- Joshua Blochによる静的ファクトリーメソッドを検討する