デザインパターン:ファクトリvsファクトリメソッドvs抽象ファクトリ


183

ウェブサイトからデザインパターンを読んでいた

そこで、ファクトリー、ファクトリーメソッド、アブストラクトファクトリーについて読みましたが、それらは非常に混乱しており、定義が明確ではありません。定義によると

ファクトリ-インスタンス化ロジックをクライアントに公開せずにオブジェクトを作成し、共通のインターフェースを介して新しく作成されたオブジェクトを参照します。ファクトリーメソッドの簡易バージョンです

ファクトリメソッド-オブジェクトを作成するためのインターフェイスを定義しますが、インスタンス化するクラスをサブクラスに決定させ、共通のインターフェイスを通じて新しく作成されたオブジェクトを参照させます。

抽象ファクトリー-クラスを明示的に指定せずに、関連オブジェクトのファミリーを作成するためのインターフェースを提供します。

Abstract FactoryとFactory Methodに関する他のスタックオーバーフロースレッドも調べましたが、そこに描画されたUMLダイアグラムは理解をさらに悪くします。

誰か教えてください

  1. これら3つのパターンはどのように違うのですか?
  2. いつ使用するのですか?
  3. また、可能であれば、これらのパターンに関連するJavaの例はありますか?

3
OPとほぼ同じ質問への回答を探していたところ、「ファクトリーなしからファクトリーメソッドへ」という記事を見つけました。サンプルプロジェクトの進化をたどることによって洞察を提供します(タイトルで言及されているファクトリーメソッドは、進化のステップの1つです)。
Nick Alexeev

回答:


251

3つのFactoryタイプはすべて同じことを行います。これらは「スマートコンストラクター」です。

リンゴとオレンジの2種類のフルーツを作成できるようにするとします。

工場

ファクトリーは「修正済み」であり、サブクラス化のない1つの実装しかありません。この場合、次のようなクラスになります。

class FruitFactory {

  public Apple makeApple() {
    // Code for creating an Apple here.
  }

  public Orange makeOrange() {
    // Code for creating an orange here.
  }

}

使用例:AppleまたはOrangeの構築は、どちらもコンストラクターで処理するには少し複雑すぎます。

ファクトリーメソッド

ファクトリメソッドは、クラスで一般的な処理を行っているが、実際に使用するフルーツの種類を変更する場合に一般的に使用されます。そう:

abstract class FruitPicker {

  protected abstract Fruit makeFruit();

  public void pickFruit() {
    private final Fruit f = makeFruit(); // The fruit we will work on..
    <bla bla bla>
  }
}

...次にFruitPicker.pickFruit()、サブクラスでファクトリメソッドを実装することにより、の共通機能を再利用できます。

class OrangePicker extends FruitPicker {

  @Override
  protected Fruit makeFruit() {
    return new Orange();
  }
}

抽象ファクトリー

抽象ファクトリは通常、「同じ種類」である必要があり、いくつかの共通の基本クラスを持つ必要があるオブジェクトのファミリ全体を作成できるようにする場合に、依存性注入/戦略などに使用されます。これは、漠然とした果物に関連した例です。ここでの使用例は、Appleで誤ってOrangePickerを使用しないようにすることです。同じ工場から果物とピッカーを入手する限り、それらは一致します。

interface PlantFactory {

  Plant makePlant();

  Picker makePicker(); 

}

public class AppleFactory implements PlantFactory {
  Plant makePlant() {
    return new Apple();
  }

  Picker makePicker() {
    return new ApplePicker();
  }
}

public class OrangeFactory implements PlantFactory {
  Plant makePlant() {
    return new Orange();
  }

  Picker makePicker() {
    return new OrangePicker();
  }
}

8
+1これは、これらのパターンに対する私の理解と最もよく似た答えです。呼び出しコード(クライアント)の例を追加することも役立ちますか?私を多く悩ませている質問は、抽象ファクトリパターンはファクトリメソッドパターンでファクトリを拡張しただけだと言えるでしょうか(これが真実であれば、このトピックは明確です)。
croraf 2014年

9
これは私が長年探し求めてきた例です。
Taztingo 2016年

それは素晴らしい説明です!ありがとう!
アンドレ・アンドラーデ

@AndréAndrade ファクトリメソッドを呼び出す方法は?小さなコードsamle pls :)これは、その使用法に関する私の疑問を
解消

25
  1. これら3つのパターンはどのように違うのですか?

ファクトリ:インスタンス化ロジックをクライアントに公開せずにオブジェクトを作成します。

ファクトリー・メソッド:オブジェクトを作成するためのインターフェースを定義しますが、インスタンス化するクラスはサブクラスに決定させます。Factoryメソッドにより、クラスはインスタンス化をサブクラスに遅らせることができます

抽象ファクトリー:具象クラスを指定せずに、関連オブジェクトまたは依存オブジェクトのファミリーを作成するためのインターフェースを提供します。

AbstractFactoryパターンはオブジェクトを作成する責任を別のクラスに委譲するために構成を使用しますが、Factoryメソッド設計パターンは継承を使用し、派生クラスまたはサブクラスに依存してオブジェクトを作成します

  1. いつ使用するのですか?

ファクトリ:クライアントはクラスを必要とするだけであり、どの具体的な実装を取得するかを気にしません。

ファクトリメソッド:クライアントは、実行時に作成する必要のある具体的なクラスを認識していませんが、ジョブを実行するクラスを取得したいだけです。

AbstactFactory: システムが複数の製品ファミリを作成する必要がある場合、または実装の詳細を公開せずに製品のライブラリを提供する場合。

Abstract Factoryクラスは、多くの場合、Factoryメソッドで実装されます。ファクトリメソッドは通常、テンプレートメソッド内で呼び出されます。

  1. また、可能であれば、これらのパターンに関連するJavaの例はありますか?

FactoryとFactoryMethod

意図:

オブジェクトを作成するためのインターフェースを定義しますが、インスタンス化するクラスはサブクラスに決定させます。ファクトリメソッドを使用すると、クラスはインスタンス化をサブクラスに遅らせることができます。

UML図

ここに画像の説明を入力してください

製品: Factoryメソッドが作成するオブジェクトのインターフェースを定義します。

ConcreteProduct: Productインターフェースを実装します

作成者: Factoryメソッドを宣言します

ConcreateCreator: Factoryメソッドを実装して、ConcreteProductのインスタンスを返します。

問題の説明:ゲームインターフェイスを定義するファクトリメソッドを使用して、ゲームのファクトリを作成します。

コードスニペット:

工場パターン。いつファクトリーメソッドを使用するのですか?

他の創造的なパターンとの比較:

  1. 設計はファクトリーメソッド(それほど複雑ではなく、よりカスタマイズ可能で、サブクラスが増殖する)を使用して開始し、デザイナーがより多くの柔軟性が必要な場所を発見すると、抽象ファクトリー、プロトタイプ、またはビルダー(より柔軟でより複雑)に進化します。

  2. 抽象ファクトリクラスは、多くの場合、ファクトリメソッドで実装されますが、プロトタイプを使用して実装することもできます。

参考資料:資料作成のデザインパターン


ファクトリーメソッドの場合、それはスーパークラスを定義すべきではありませんか?
トニー・林

21

Factory-複雑なオブジェクトを作成するための別個のFactoryクラス。

例:Fruitのオブジェクトを作成するFruitFactoryクラス

class FruitFactory{

public static Fruit getFruit(){...}

}

ファクトリーメソッド - ファクトリー用の個別のクラス全体ではなく、そのクラス自体に1つのメソッドをファクトリーとして追加するだけです。

例:

Calendar.getInstance() (Java's Calendar)

抽象ファクトリメソッド -Factory of Factory

例:コンピューター部品の工場を建設したいとしましょう。したがって、ラップトップ、デスクトップ、サーバーなど、いくつかのタイプのコンピューターがあります。

したがって、各compterタイプにはファクトリが必要です。そこで、以下のような工場の高レベルの工場を1つ作成します

ComputerTypeAbstractFactory.getComputerPartFactory(String computerType) ---> This will return PartFactory which can be one of these ServerPartFactory, LaptopPartFactory, DesktopPartFactory.

現在、これら3つ自体は再び工場です。(PartFactory自体を処理しますが、内部では、抽象ファクトリで提供したものに基づいて個別の実装があります)

  Interface-> PartFactory. getComputerPart(String s), 
Implementations -> ServerPartFactory, LaptopPartFactory, DesktopPartFactory.

Usage:
new ComputerTypeAbstractFactory().getFactory(“Laptop”).getComputerPart(“RAM”)

編集:コメントの反対意見に従って、抽象ファクトリーの正確なインターフェースを提供するように編集されました。


1
の背後にあるアイデアComputerFactoryは、一般的な作成インターフェイス(getScreen(); getKeyboard(); getDiskdrive(); ...)であり、コンピュータタイプごとのインターフェイスではないということです。同じステートメントで同じ単語を2回使用すると、デザインの問題を嗅ぐことができます:Laptop Factory.get Laptop Part()。
xtofl 2012年

いやいやいや、コードそのものに的を絞ってはいけません。それは理解のための単なる学問でした。インターフェースの正確な例が必要な場合は、こちらをご覧ください。オブジェクト:インターフェイス-> ComputerPart、実装-> RAM、HDD、プロセッサフ​​ァクトリ:インターフェイス-> PartFactory。getComputerPart(String s)、実装-> ServerPartFactory、LaptopPartFactory、DesktopPartFactory。抽象ファクトリ:ComputerType.getPartFactory(“ String s”)使用法:new ComputerType()。getFactory(“ Laptop”)。getComputerPart(“ RAM”)
Ravi K

2
私はあなたの懸念に対処するために回答を更新しました。実際に抽象的なファクトリーは、ファクトリーの事実にすぎません。参考のために、以前の例を示しました(読者が実際の実装中にインターフェースを処理すると仮定します)。通知してくれてありがとう。改善することは常に良いことです。:)
Ravi K

いいえabstract Factoryはファクトリーのファクトリーではありません... さまざまな具体的なファクトリーで実装/拡張されるオブジェクトを作成abstract classするinterfaceことができます。コードの詳細については、承認された回答を参照してください。そして、それに応じて回答を削除または編集してください。
Julien__ 2016

私はファクトリーメソッドの説明が好きです。ファクトリーメソッドは、なぜそのように命名されているのかを明らかにするのに十分簡潔です。このパターンでは、ファクトリはメソッドであり、インスタンス化メソッドをグループ化するヘルパーユーティリティではないが、それ自体では意味のあるクラスではありません。残念ながら、他のより複雑な答えはこの点を逃しました。
wlnirvana

11

すべてのデザインパターンは、記述された動作中のコードに触れないようにするために成功しています。作業コードに触れると、既存の作業フローに欠陥があり、何も壊していないことを確認するにはさらに多くのテストを行う必要があることは誰もが知っています。

ファクトリパターンは、入力基準に基づいてオブジェクトを作成します。これにより、このようなオブジェクトを作成したり、このオブジェクトを作成したりするようなコードを記述する必要がなくなります。これの良い例は旅行ウェブサイトです。旅行ウェブサイトは、旅行(フライト、電車、バス)を提供するか、/およびホテルを提供するか、および/または観光名所パッケージを提供することができます。ユーザーが次を選択すると、Webサイトは作成する必要のあるオブジェクトを決定する必要があります。旅行またはホテルのオブジェクトも作成する必要があります。

これで、ポートフォリオに別のWebサイトを追加することを想定していて、同じコアが使用されていると思われる場合(たとえば、現在はタクシーを検索してオンラインで支払いを行うカープーリングWebサイト)、コアで抽象的なファクトリを使用できます。このようにして、タクシーとカープールのもう1つの工場でスナップすることができます。

どちらの工場も互いに関係がないため、別の工場に保管するのに適した設計です。

これが今明らかであることを願っています。この例を念頭に置いて、もう一度ウェブサイトを調べてください。うまくいけば役立つでしょう。そして、私はパターンを正しく表現したことを本当に願っています:)。


3

この答えについては、「ギャングオブフォー」の本を参照します。

ありません何の本の中で「工場」や「シンプルファクトリー」や「バーチャル工場」の定義は。通常、人々が「ファクトリー」パターンについて話しているとき、彼らはクラスの特定のオブジェクトを作成する何かについて話しているかもしれません(「ビルダー」パターンではありません)。彼らはそうかもしれないし、そうでないかもしれない、「ファクトリーメソッド」または「抽象ファクトリー」パターンを参照する場合があります。「ファクトリー」は正式な用語ではないため、誰もが実装することはできません(一部の人々\企業\コミュニティは独自の語彙を持つことができることに注意してください)。

この本には、「Abstract Factory」と「Factory Method」の定義のみが含まれています。

ここでは、本の定義と、両方が非常に混乱する理由を簡単に説明します。他の回答で見つけることができるので、コード例は省略します。

ファクトリメソッド(GOF):オブジェクトを作成するためのインターフェースを定義しますが、インスタンス化するクラスはサブクラスに決定させます。ファクトリメソッドを使用すると、クラスはインスタンス化をサブクラスに遅らせることができます。

Abstract Factory(GOF):具体的なクラスを指定せずに、関連オブジェクトまたは依存オブジェクトのファミリを作成するためのインターフェースを提供します。

混乱の原因:多くの場合、「ファクトリーメソッド」パターンで使用されたクラスを「ファクトリー」として呼び出すことができます。このクラスは、定義により抽象です。そのため、このクラスを「抽象ファクトリ」と呼ぶのは簡単です。しかし、それはクラスの名前にすぎません。「抽象ファクトリ」パターン(クラス名!=パターン名)と混同しないでください。「抽象ファクトリ」パターンは異なります- 抽象クラスを使用しませ。それは、相互に関連する、または特定の方法で作成する必要がある、より大きなオブジェクトの一部を作成するためのインターフェース(必ずしもプログラミング言語インターフェースではない)を定義します。


2
AbstractProductA, A1 and A2 both implementing the AbstractProductA
AbstractProductB, B1 and B2 both implementing the AbstractProductB

interface Factory {
    AbstractProductA getProductA(); //Factory Method - generate A1/A2
}

ファクトリメソッドを使用すると、AbstractProductAのA1またはA2を作成できます。

interface AbstractFactory {
    AbstractProductA getProductA(); //Factory Method
    AbstractProductB getProductB(); //Factory Method
}

ただし、抽象ファクトリには複数のファクトリメソッド(例:2つのファクトリメソッド)があり、これらのファクトリメソッドを使用して、オブジェクト/関連オブジェクトのセットを作成します。Abstract Factoryを使用すると、AbstractProductA、AbstractProductBのA1、B1オブジェクトを作成できます。


0

オリジナルの本「Design Patterns:Elements of Reusable Object-Oriented Software」を引用した人は誰もいません。このセクションでは、「創造的なパターンのディスカッション」セクションの最初の2つの段落でその答えを示しています(強調は私)。

作成するオブジェクトのクラスによってシステムをパラメーター化するには、2つの一般的な方法があります。1つの方法は、オブジェクトを作成するクラスをサブクラス化することです。これは、ファクトリーメソッド(107)パターンの使用に対応します。このアプローチの主な欠点は、製品のクラスを変更するためだけに新しいサブクラスが必要になる可能性があることです。このような変更はカスケードする可能性があります。たとえば、製品の作成者自身がファクトリメソッドによって作成されている場合、その作成者もオーバーライドする必要があります。

システムをパラメーター化するもう1つの方法は、オブジェクトの構成に依存します。製品オブジェクトのクラスを認識する責任があるオブジェクトを定義し、それをシステムのパラメーターにします。これは、Abstract Factory(87)、Builder(97)、およびPrototype(117)パターンの重要な側面です。3つすべては、製品オブジェクトの作成を担当する新しい「ファクトリオブジェクト」の作成を伴います。Abstract Factoryには、いくつかのクラスのオブジェクトを生成するファクトリオブジェクトがあります。Builderには、対応する複雑なプロトコルを使用して段階的に複雑な製品を構築するファクトリオブジェクトがあります。プロトタイプには、プロトタイプオブジェクトをコピーして製品を構築するファクトリオブジェクトがあります。この場合、プロトタイプは製品を返す責任があるため、ファクトリオブジェクトとプロトタイプは同じオブジェクトです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.