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

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

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の例は、デザインパターンという書籍にある例と似ています
は、具体的なクラスを作成するサブクラスにオブジェクト
の作成を委譲し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 (); print $car -> getType ();このPythonの例では、前のJavaの例と同じものを使用しています。
from abc import ABC , abstractmethod クラス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 は、デザインパターンにはこれと直接同等のものは存在しないと主張しています。
注記
- ^ Gamma他 1995年、107ページ
- ^ Gamma, Erich、Helm, Richard、John Johnson, Ralph、Vlissides, John (1995).デザインパターン:再利用可能なオブジェクト指向ソフトウェアの要素. Addison-Wesley. ISBN 0-201-63361-2。
- ^ エリック・フリーマン、エリザベス・ロブソン、キャシー・シエラ、バート・ベイツ (2004)。マイク・ヘンドリクソン、マイク・ルーキデス (編)。『Head First Design Patterns: A Brain-Friendly Guide』(ペーパーバック)。第1巻(第1版)。オライリー・メディア。162ページ。ISBN 978-0-596-00712-62012年9月12日閲覧
- ^ 「ファクトリーメソッド設計パターン - 構造とコラボレーション」w3sDesign.com 2017年8月12日閲覧
- ^ ガンマら1995年、122ページ。
参考文献
- マーティン・ファウラー、ケント・ベック、ジョン・ブラント、ウィリアム・オプダイク、ドン・ロバーツ(1999年6月)。『リファクタリング:既存コードの設計改善』アディソン・ウェスレー、ISBN 0-201-48567-2。
- コックス、ブラッド・J. (1986).オブジェクト指向プログラミング:進化的アプローチ. アディソン・ウェスレー. ISBN 978-0-201-10393-9。
- コーエン、タル、ギル、ジョセフ (2007).「ファクトリーによるより良い建設」(PDF) .ジャーナル・オブ・オブジェクト・テクノロジー. 6 (6).ベルトラン・マイヤー: 103. doi : 10.5381/jot.2007.6.6.a3 . 2007年3月12日閲覧
外部リンク
- ファクトリーデザインパターン 2018年1月10日アーカイブWayback Machine Javaでの実装
- UMLとLePUS3(設計記述言語)におけるファクトリーメソッド
- Joshua Blochによる静的ファクトリーメソッドを検討する