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


回答:


387

私は自分のクラスを「人」であるという観点からデザインパターンについて考えるのが好きです。パターンは、人々がお互いに話す方法です。

ですから、私にとっては、工場のパターンは採用代理店のようなものです。可変数のワーカーを必要とする人がいます。この人は彼らが雇う人々に必要ないくつかの情報を知っているかもしれませんが、それだけです。

そのため、新しい従業員が必要になったとき、彼らは採用代理店に電話して、必要なものを伝えます。さて、実際に誰かを雇うには、多くのことを理解する必要があります-利点、適格性の検証など。しかし、雇用する人はこれを知る必要はありません-雇用機関がすべてを処理します。

同様に、ファクトリを使用すると、コンシューマは、作成方法や依存関係の詳細を知らなくても、新しいオブジェクトを作成できます。実際に必要な情報を提供するだけで済みます。

public interface IThingFactory
{
    Thing GetThing(string theString);
}

public class ThingFactory : IThingFactory
{
    public Thing GetThing(string theString)
    {
        return new Thing(theString, firstDependency, secondDependency);
    }
}

そのため、ThingFactoryのコンシューマーはThingを取得できます。コンシューマーからの文字列データを除いて、Thingの依存関係を知る必要はありません。


17
GetThing()の具体的な実装は、firstDependencyとsecondDependencyの値をどこで取得しますか?
Mikeyg36 2013年

88
これがOPの質問にどのように答えるかを誰かに教えてもらえますか?これは単に「ファクトリーパターン」とは何かを説明し、次に3つの「ファクトリーパターン」の1つである「ファクトリーメソッド」の例を追加するだけです。つまり、どこにも比較がありません。
Forethinker 2013

4
OPの質問には明確に言及していますwithin an object instead of a Factory class。私は彼がctorをプライベートにし、静的メソッドを使用してクラスをインスタンス化する(オブジェクトを作成する)シナリオを意味したと思います。ただし、この例に従うには、ThingFactoryまずクラスをインスタンス化してThingオブジェクトを取得する必要がありますFactory class。これにより、これが有効になります。
atiyar 2014

4
申し訳ありませんが、依存関係を隠す方法でコンストラクタを書くこともできるので、説明はくだらないです。依存関係の作成の情報を依存関係の管理から分離したい重要な背景情報がありません。その上、質問は同じクラス内についてでした、答えはそれに関連する方法ではありません。
クリスチャンHujer

8
OPはいつ尋ね。杏龍はどう答えた。答えのスタイルは立派ですが、この質問の文脈では、それは単なるノイズです。
8bitjunkie 2015年

96

ファクトリーメソッドは、コンストラクターの代替として考える必要があります-ほとんどの場合、コンストラクターが十分に表現力がない場合、

class Foo{
  public Foo(bool withBar);
}

次のような表現力はありません:

class Foo{
  public static Foo withBar();
  public static Foo withoutBar();
}

ファクトリクラスは、オブジェクトを作成するための複雑なプロセスが必要な場合、実際のクラスには不要な依存関係を作成する必要がある場合、異なるオブジェクトを作成する必要がある場合などに役立ちます。


2
ここでFactoryクラスはどこにありますか?
Koray Tugay 14

20
@KorayTugay:ファクトリクラスはなく、ファクトリメソッドのみです。問題は、ファクトリクラスではなくファクトリメソッドをいつ使用するかです。ただし、ファクトリーメソッドは、ファクトリークラスの代わりよりもコンストラクターの代わりになります。(ファクトリークラスについてのみ話しているにもかかわらず、なぜトップアンサーが非常に高く評価されるのかはわかりません)。
Rasmus Faber 14

5
静的ファクトリー・メソッドは、ギャング・オブ・フォー:ファクトリー・メソッドの設計パターンとは完全に異なることに注意してください。
jaco0646 2016

76

私が個人的に意味のある別個のFactoryクラスを見つけた1つの状況は、作成しようとしている最後のオブジェクトが他のいくつかのオブジェクトに依存している場合です。例えば、PHPで:あなたが持っていると仮定House順番に持つオブジェクト、KitchenおよびLivingRoomオブジェクトを、そしてLivingRoomオブジェクトがありTV、オブジェクトの内部を同様。

これを実現する最も簡単な方法は、各オブジェクトがそれらの構築メソッドで子を作成することですが、プロパティが比較的ネストされているHouse場合、作成に失敗すると、おそらく何が失敗しているのかを特定するためにある程度の時間を費やすことになります。

別の方法は、次のことを行うことです(ファンシー用語が好きな場合は、依存性注入):

$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);

ここで、作成プロセスがHouse失敗した場合、確認する場所は1つだけですが、新しいチャンクが必要になるたびにこのチャンクを使用する必要があるのHouseは、あまり便利ではありません。工場を入力してください:

class HouseFactory {
    public function create() {
        $TVObj = new TV($param1, $param2, $param3);
        $LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
        $KitchenroomObj = new Kitchen($param1, $param2);
        $HouseObj = new House($LivingroomObj, $KitchenroomObj);

        return $HouseObj;
    }
}

$houseFactory = new HouseFactory();
$HouseObj = $houseFactory->create();

ここのファクトリーのおかげで、を作成するプロセスHouseは抽象化され(を作成したいだけのときにすべての依存関係を作成して設定する必要がないためHouse)、同時に集中化されるため、保守が容易になります。別個のファクトリーを使用することが有益である理由は他にもあります(例えば、テスト容易性)が、この特定のユースケースを見つけて、ファクトリークラスがどのように役立つかを最もよく示しています。


1
誰かがこれについてユニットテストをどのように行うのですか?クラスで "new"キーワードを使用することは、単体テストを行うことができないため、悪い習慣だと考えられました。それとも工場はそのルールの少しの例外であることを意図していますか?
AgmLauncher 2013年

1
@AgmLauncher私も、私はユニットテストを開始してしまったときに、同じ質問を持っていた、チェックアウト:stackoverflow.com/questions/10128780/...
Mahn

1
これを取得できませんでした。さまざまなオブジェクトを作成するためのパラメーターがHouseFactoryクラスにどの程度正確に渡されますか?
atiyar 2014

1
@マーン、結局あなたはたくさんのパラメーターを持っているのではないでしょうか?
Pacerier 2014年

1
@Pacerierは、必要に応じてモデル化の方法を決定するためのものですが、必ずしもすべてのパラメーターをcreateメソッドに渡す必要はありません。たとえば、House常に同じ種類を使用LivingRoomする場合は、引数として渡されるのではなく、ファクトリクラスでパラメータをハードコードする方が理にかなっています。または、数種類のがあり、内部にスイッチがあり、それぞれのタイプごとにパラメーターがハードコーディングされている場合は、メソッドにtype引数を提供することもできます。HouseFactory::createLivingRoom
Mahn

19

ファクトリーまたはファクトリーメソッドを使用する背後にあるアイデアを明確に区別することが重要です。どちらも、相互に排他的な異なる種類のオブジェクト作成問題に対処するためのものです。

「ファクトリーメソッド」について具体的に説明しましょう。

最初に、ライブラリまたはAPIを開発していて、それをさらにアプリケーション開発に使用する場合、ファクトリーメソッドは作成パターンの最適な選択肢の1つです。背後にある理由。必要な機能のオブジェクトをいつ作成するかはわかりますが、オブジェクトのタイプは未定のままか、渡される動的パラメーターによって決定されます。

今のポイントは、ファクトリーパターン自体を使用してもほぼ同じことが達成できますが、ファクトリーパターンが上記の強調表示された問題に使用される場合、システムに大きな欠点が1つ導入されます。つまり、異なるオブジェクト(サブクラスオブジェクト)を作成するロジックが特定のビジネス状況に固有であるため、将来、他のプラットフォーム用にライブラリの機能を拡張する必要がある場合(より技術的には、基本インターフェースまたは抽象クラスのサブクラスを追加して、ファクトリが既存のオブジェクトに加えてそれらのオブジェクトも返すようにする必要があります)いくつかの動的パラメーターに基づいて)その後、ファクトリクラスのロジックを変更(拡張)する必要があるたびに、操作のコストが高くなり、設計の観点からは良くありません。逆に、「工法」なら

interface Deliverable 
{
    /*********/
}

abstract class DefaultProducer 
{

    public void taskToBeDone() 
    {   
        Deliverable deliverable = factoryMethodPattern();
    }
    protected abstract Deliverable factoryMethodPattern();
}

class SpecificDeliverable implements Deliverable 
{
 /***SPECIFIC TASK CAN BE WRITTEN HERE***/
}

class SpecificProducer extends DefaultProducer 
{
    protected Deliverable factoryMethodPattern() 
    {
        return new SpecificDeliverable();
    }
}

public class MasterApplicationProgram 
{
    public static void main(String arg[]) 
    {
        DefaultProducer defaultProducer = new SpecificProducer();
        defaultProducer.taskToBeDone();
    }
}

15

これらは、同じパラメータータイプで動作が異なる複数の「コンストラクター」が必要な場合にも役立ちます。


15

次の場合は、オブジェクト内でファクトリメソッドを使用することをお勧めします。

  1. オブジェクトのクラスは、作成する必要がある正確なサブクラスを認識していません
  2. オブジェクトのクラスは、それが作成するオブジェクトがサブクラスによって指定されるように設計されています
  3. オブジェクトのクラスは、その義務を補助的なサブクラスに委任し、どのクラスがこれらの義務を果たすのかを知らない

次の場合は、抽象ファクトリクラスを使用することをお勧めします。

  1. オブジェクトは、その内部オブジェクトがどのように作成および設計されているかに依存すべきではありません
  2. リンクされたオブジェクトのグループは一緒に使用する必要があり、この制約を提供する必要があります
  3. オブジェクトは、親オブジェクトの一部となる、リンクされたオブジェクトのいくつかの可能なファミリーの1つによって構成する必要があります
  4. 実装のみではなくインターフェースのみを示す子オブジェクトを共有する必要があります

9

からのUML

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

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

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

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

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

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

コードスニペット:

import java.util.HashMap;


/* Product interface as per UML diagram */
interface Game{
    /* createGame is a complex method, which executes a sequence of game steps */
    public void createGame();
}

/* ConcreteProduct implementation as per UML diagram */
class Chess implements Game{
    public Chess(){

    }
    public void createGame(){
        System.out.println("---------------------------------------");
        System.out.println("Create Chess game");
        System.out.println("Opponents:2");
        System.out.println("Define 64 blocks");
        System.out.println("Place 16 pieces for White opponent");
        System.out.println("Place 16 pieces for Black opponent");
        System.out.println("Start Chess game");
        System.out.println("---------------------------------------");
    }
}
class Checkers implements Game{
    public Checkers(){

    }
    public void createGame(){
        System.out.println("---------------------------------------");
        System.out.println("Create Checkers game");
        System.out.println("Opponents:2 or 3 or 4 or 6");
        System.out.println("For each opponent, place 10 coins");
        System.out.println("Start Checkers game");
        System.out.println("---------------------------------------");
    }
}
class Ludo implements Game{
    public Ludo(){

    }
    public void createGame(){
        System.out.println("---------------------------------------");
        System.out.println("Create Ludo game");
        System.out.println("Opponents:2 or 3 or 4");
        System.out.println("For each opponent, place 4 coins");
        System.out.println("Create two dices with numbers from 1-6");
        System.out.println("Start Ludo game");
        System.out.println("---------------------------------------");
    }
}

/* Creator interface as per UML diagram */
interface IGameFactory {
    public Game getGame(String gameName);
}

/* ConcreteCreator implementation as per UML diagram */
class GameFactory implements IGameFactory {

     HashMap<String,Game> games = new HashMap<String,Game>();
    /*  
        Since Game Creation is complex process, we don't want to create game using new operator every time.
        Instead we create Game only once and store it in Factory. When client request a specific game, 
        Game object is returned from Factory instead of creating new Game on the fly, which is time consuming
    */

    public GameFactory(){

        games.put(Chess.class.getName(),new Chess());
        games.put(Checkers.class.getName(),new Checkers());
        games.put(Ludo.class.getName(),new Ludo());        
    }
    public Game getGame(String gameName){
        return games.get(gameName);
    }
}

public class NonStaticFactoryDemo{
    public static void main(String args[]){
        if ( args.length < 1){
            System.out.println("Usage: java FactoryDemo gameName");
            return;
        }

        GameFactory factory = new GameFactory();
        Game game = factory.getGame(args[0]);
        if ( game != null ){                    
            game.createGame();
            System.out.println("Game="+game.getClass().getName());
        }else{
            System.out.println(args[0]+  " Game does not exists in factory");
        }           
    }
}

出力:

java NonStaticFactoryDemo Chess
---------------------------------------
Create Chess game
Opponents:2
Define 64 blocks
Place 16 pieces for White opponent
Place 16 pieces for Black opponent
Start Chess game
---------------------------------------
Game=Chess

この例は、Factoryを実装することによるクラスを示していFactoryMethodます。

  1. Gameあらゆる種類のゲームのインターフェースです。それは複雑な方法を定義します:createGame()

  2. Chess, Ludo, Checkers ゲームのさまざまなバリアントであり、 createGame()

  3. public Game getGame(String gameName)あるFactoryMethod中でIGameFactory、クラス

  4. GameFactoryコンストラクターでさまざまなタイプのゲームを事前に作成します。IGameFactoryファクトリメソッドを実装しています。

  5. ゲーム名はコマンドライン引数として渡されます NotStaticFactoryDemo

  6. getGamein GameFactoryはゲーム名を受け入れ、対応するGameオブジェクトを返します。

工場:

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

FactoryMethod

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

使用事例:

いつ使用するか:Client実行時に作成する必要がある具体的なクラスがわからないが、仕事をするクラスを取得したいだけ。


キーノートセクションをありがとう、それは私にとっては簡潔です。しかし、「getArea()はShapeインタフェースのファクトリメソッド」であるとは想像できません。メソッドgetAreaは決してクラスをインスタンス化しないため、計算を行うだけです。オブジェクトを作成するためのインターフェイスですが、インスタンス化するクラスをサブクラスに決定させてください。」
reco

getArea()はファクトリーメソッドはありません。
クラニオ2018

1
私は別の意見を持っています-専門家が検証してメモを付けてください。1.クライアント(または呼び出し元)には対象オブジェクトが必要です...したがって、新しいGameFactory()を呼び出す必要はありません。ファクトリクラスには静的getInstance()が必要です。2。また、そうであれば、 games.put(Chess.class.getName( )、new Chess()); 常にチェスの同じ参照を返します[静的として実装されている場合]-そのシナリオを最も効果的に処理する方法は?
Arnab Dutta

非静的ファクトリーの例を示しました。必要に応じて、静的ブロックとメソッドを使用して実装できます。あなたの質問について:1。クライアントはファクトリーに電話してゲームを入手します。2.オブジェクトを1回配置すると、すべてのGetが同じインスタンスを返します
-Gets

6

それは本当に好みの問題です。ファクトリークラスは必要に応じて抽象化/インターフェース化できますが、ファクトリーメソッドは軽量です(定義された型がないため、テストしやすい傾向がありますが、サービスと同様に既知の登録ポイントが必要になります)ロケータですが、ファクトリメソッドの検索用です)。


4

ファクトリクラスは、返されるオブジェクトタイプにプライベートコンストラクタがある場合、異なるファクトリクラスが返されるオブジェクトに異なるプロパティを設定する場合、または特定のファクトリタイプが返される具象タイプと結合されている場合に役立ちます。

WCFはServiceHostFactoryクラスを使用して、さまざまな状況でServiceHostオブジェクトを取得します。IISは.svcファイルのServiceHostインスタンスを取得するために標準のServiceHostFactoryを使用しますが、JavaScriptクライアントにシリアル化を返すサービスにはWebScriptServiceHostFactoryを使用します。ADO.NET Data Servicesには独自の特別なDataServiceHostFactoryがあり、ASP.NETにはそのサービスにプライベートコンストラクターがあるため、ApplicationServicesHostFactoryがあります。

ファクトリを消費するクラスが1つしかない場合は、そのクラス内でファクトリメソッドを使用できます。


2

OrderおよびCustomerクラスを設計する必要があるシナリオを検討してください。簡素化と初期要件のために、Orderクラスのファクトリの必要性を感じず、アプリケーションに多くの「new Order()」ステートメントを入力します。物事はうまく機能しています。

ここで、CustomerアソシエーションなしではOrderオブジェクトをインスタンス化できない(新しい依存関係)という新しい要件が登場します。今、あなたは以下の考慮事項を持っています。

1-新しい実装でのみ機能するコンストラクタオーバーロードを作成します。(受け付けできません)。2-あなたはOrder()署名を変更し、すべての呼び出しを変更します。(良い習慣ではなく、本当の苦痛です)。

代わりに、注文クラスのファクトリを作成した場合は、コードを1行変更するだけで済みます。ほとんどすべての集合体の関連付けにはFactoryクラスをお勧めします。お役に立てば幸いです。


1

使用に関して別のオブジェクトを作成する場合。それは便利です。

public class factoryMethodPattern {
      static String planName = "COMMERCIALPLAN";
      static int units = 3;
      public static void main(String args[]) {
          GetPlanFactory planFactory = new GetPlanFactory();
          Plan p = planFactory.getPlan(planName);
          System.out.print("Bill amount for " + planName + " of  " + units
                        + " units is: ");
          p.getRate();
          p.calculateBill(units);
      }
}

abstract class Plan {
      protected double rate;

      abstract void getRate();

      public void calculateBill(int units) {
            System.out.println(units * rate);
      }
}

class DomesticPlan extends Plan {
      // @override
      public void getRate() {
            rate = 3.50;
      }
}

class CommercialPlan extends Plan {
      // @override
      public void getRate() {
            rate = 7.50;
      }
}

class InstitutionalPlan extends Plan {
      // @override
      public void getRate() {
            rate = 5.50;
      }
}

class GetPlanFactory {

      // use getPlan method to get object of type Plan
      public Plan getPlan(String planType) {
            if (planType == null) {
                  return null;
            }
            if (planType.equalsIgnoreCase("DOMESTICPLAN")) {
                  return new DomesticPlan();
            } else if (planType.equalsIgnoreCase("COMMERCIALPLAN")) {
                  return new CommercialPlan();
            } else if (planType.equalsIgnoreCase("INSTITUTIONALPLAN")) {
                  return new InstitutionalPlan();
            }
            return null;
      }
}


1

私はあなたがあなたのコードに持ち込みたい疎結合度に依存すると思います。

ファクトリメソッドは物事を非常にうまく分離しますが、ファクトリクラスはそうではありません。

つまり、ファクトリメソッドを使用する方が、単純なファクトリ(ファクトリクラスと呼ばれる)を使用するよりも変更が簡単です。

この例を見てくださいhttps : //connected2know.com/programming/java-factory-pattern/。今、あなたは新しい動物を連れて行きたいと想像してください。FactoryクラスではFactoryを変更する必要がありますが、factoryメソッドでは、新しいサブクラスを追加するだけで済みます。


0

ファクトリクラスはより重いですが、特定の利点があります。複数の生データソースからオブジェクトを構築する必要がある場合は、1つの場所に構築ロジック(およびおそらくデータの集計)のみをカプセル化できます。そこでは、オブジェクトインターフェイスに関係なく、抽象的にテストできます。

これは、特にORMを置き換えたり不十分だったりすることができず、DBテーブルの結合やストアドプロシージャから多くのオブジェクトを効率的にインスタンス化したい場合に便利なパターンです。


0

私は工場を図書館の概念にたとえています。たとえば、数値を処理するためのライブラリと、図形を処理するためのライブラリを用意できます。これらのライブラリの関数は、論理的に名前が付けられたディレクトリに、NumbersまたはShapesます。これらは、形状の場合、整数、フロート、ドーブル、ロングまたは長方形、円、三角形、五角形を含むことができる一般的なタイプです。

ファクトリペッターは、ポリモーフィズム、依存関係の注入、および制御の反転を使用します。

ファクトリーパターンの目的は次のとおりです。 Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

したがって、オペレーティングシステムまたはフレームワークを構築していて、すべての個別コンポーネントを構築しているとしましょう。

PHPのFactoryパターンの概念の簡単な例を次に示します。私はそれのすべてで100%ではないかもしれませんが、それは簡単な例として役立つことを意図しています。私は専門家ではありません。

class NumbersFactory {
    public static function makeNumber( $type, $number ) {
        $numObject = null;
        $number = null;

        switch( $type ) {
            case 'float':
                $numObject = new Float( $number );
                break;
            case 'integer':
                $numObject = new Integer( $number );
                break;
            case 'short':
                $numObject = new Short( $number );
                break;
            case 'double':
                $numObject = new Double( $number );
                break;
            case 'long':
                $numObject = new Long( $number );
                break;
            default:
                $numObject = new Integer( $number );
                break;
        }

        return $numObject;
    }
}

/* Numbers interface */
abstract class Number {
    protected $number;

    public function __construct( $number ) {
        $this->number = $number;
    }

    abstract public function add();
    abstract public function subtract();
    abstract public function multiply();
    abstract public function divide();
}
/* Float Implementation */
class Float extends Number {
    public function add() {
        // implementation goes here
    }

    public function subtract() {
        // implementation goes here
    }

    public function multiply() {
        // implementation goes here
    }

    public function divide() {
        // implementation goes here
    }
}
/* Integer Implementation */
class Integer extends Number {
    public function add() {
        // implementation goes here
    }

    public function subtract() {
        // implementation goes here
    }

    public function multiply() {
        // implementation goes here
    }

    public function divide() {
        // implementation goes here
    }
}
/* Short Implementation */
class Short extends Number {
    public function add() {
        // implementation goes here
    }

    public function subtract() {
        // implementation goes here
    }

    public function multiply() {
        // implementation goes here
    }

    public function divide() {
        // implementation goes here
    }
}
/* Double Implementation */
class Double extends Number {
    public function add() {
        // implementation goes here
    }

    public function subtract() {
        // implementation goes here
    }

    public function multiply() {
        // implementation goes here
    }

    public function divide() {
        // implementation goes here
    }
}
/* Long Implementation */
class Long extends Number {
    public function add() {
        // implementation goes here
    }

    public function subtract() {
        // implementation goes here
    }

    public function multiply() {
        // implementation goes here
    }

    public function divide() {
        // implementation goes here
    }
}

$number = NumbersFactory::makeNumber( 'float', 12.5 );

ここで何が起こっているのかは理解していますが、その意味がわかりません。何されるNumbersFactory::makeNumber( 'float', 12.5 );だけ言っオーバー私を与えるnew Float(12.5);、私は私が必要と知っていればFloat?これは私が工場について理解していないことです...ポイントは何ですか?
BadHorsie 2015年

これにより、さまざまな実装を選択でき、1つだけにバインドされることはありません。インターフェースが確立され、すべての実装はそれを保証し、尊重する必要があります。
Robert Rocha、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.