web-dev-qa-db-ja.com

工場パターン。ファクトリメソッドを使用する場合

Factoryクラスの代わりにオブジェクト内でファクトリメソッドを使用するのはいつよい考えですか?

253
jjshell

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

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

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

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

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

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

そのため、ThingFactoryのコンシューマーは、Thingの依存関係について知る必要なく、ThingFactoryを取得できます。ただし、コンシューマーから送信される文字列データは例外です。

367
kyoryu

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

class Foo{
  public Foo(bool withBar);
}

次のように表現力豊かではありません:

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

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

86
Rasmus Faber

個人的に意味のある個別の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を作成するだけの場合、すべての依存関係を作成およびセットアップする必要はありません)、同時に集中化され、保守が容易になります。 。別のファクトリを使用することが有益になる理由は他にもありますが(テスト容易性など)、この特定のユースケースは、Factoryクラスがどのように役立つかを最もよく説明するものです。

68
Mahn

工場または工場の方法を使用する背後にある考え方を明確に区別することが重要です。両方とも、相互に排他的な異なる種類のオブジェクト作成の問題に対処するためのものです。

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

最初に、ライブラリまたは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();
    }
}
16
Prakash Chhipa

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

14
Rik

次の場合にオブジェクト内でfactory methodsを使用することをお勧めします。

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

次の場合にabstract factory classを使用することをお勧めします。

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

からのUML

enter image description here

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

ConcreteProduct:製品インターフェイスの実装

Creator:Factoryメソッドを宣言します

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

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

コードスニペット:

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 factoryメソッドを実装します。

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

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

工場:

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

FactoryMethod

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

ユースケース:

使用する場合:Clientは、実行時に作成する必要のある具体的なクラスを知りませんが、ジョブを実行するクラスを取得したいだけです。

8
Ravindra babu

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

5
Brad Wilson

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

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

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

4
Mark Cidade

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

これで、Customerの関連付けなしでOrderオブジェクトをインスタンス化できないという新しい要件が明らかになりました(新しい依存関係)。現在、次の考慮事項があります。

1-新しい実装でのみ機能するコンストラクターオーバーロードを作成します。 (受け入れられません)。 2- Order()署名を変更し、すべての呼び出しを変更します。 (良い習慣と実際の痛みではありません)。

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

3
Muhammad Awais

ソースメイキングWebサイトによると、その意図は次のとおりです。

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

  • 「仮想」コンストラクターの定義。

  • 新しいオペレーターは有害と考えました。

使用方法の例:

abstract class AbstractFactoryMethod {
    abstract function makePHPBook($param);
}

class OReillyFactoryMethod extends AbstractFactoryMethod
{
    function makePHPBook($param)
    {
        $book = NULL;  
        switch ($param) {
            case "us":
                $book = new OReillyPHPBook();
            break;
            // Other classes...
            case "other":
                $book = new SamsPHPBook();
            break;
            default:
                $book = new OReillyPHPBook();
            break;        
    }

    return $book;
}

そして、テスト:

function testFactoryMethod($factoryMethodInstance)
{
    $phpUs = $factoryMethodInstance->makePHPBook("us");
    echo 'us php Author: '.$phpUs->getAuthor();
    echo 'us php Title: '.$phpUs->getTitle();
}

echo 'Testing OReillyFactoryMethod';
$factoryMethodInstance = new OReillyFactoryMethod();
testFactoryMethod($factoryMethodInstance);
3
Alexander Beat

オブジェクトの作成を、作業が必要なオブジェクトのサブクラスに延期するクラスは、Factoryパターンの例として見ることができます。

https://stackoverflow.com/a/49110001/5041 の別の回答で詳細に言及しました

1
nits.kk

私は工場を図書館の概念に例えています。たとえば、数字を操作するためのライブラリと、図形を操作するためのライブラリを用意できます。これらのライブラリの関数は、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のファクトリパターンの概念の簡単な例を次に示します。すべてが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 );
0
Robert Rocha

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

これは有用なパターンであり、特にORMを置換および不十分にすることができず、DBテーブル結合またはストアドプロシージャから多くのオブジェクトを効率的にインスタンス化する場合に役立ちます。

0
jonfm

AbstractFactoryの例

    TypeImpl<String> type = new TypeImpl<>();
    type.addType("Condition");
    type.addType("Hazardous");

    AbstractTypeFactory<String, Tag> tags = new AbstractTypeFactory<String, Tag>(type) {

        @Override
        public Tag create(String string) {
            String tp = type.find(string);

            switch (tp) {
                case "Hazardous":
                    return new HazardousTag();
                case "Condition":
                    return new ConditionTag();
                default:
                    return null;
            }
        }
    };

    Tag tagHazardous = tags.create("Hazardous");
    Tag tagCondition = tags.create("Condition");

}
0
Vahe Gharibyan

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

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;
      }
}
0
Samet öztoprak