2010-11-30 7 views
2

ウィキペディアから撮影:オブジェクト指向のコンピュータプログラミング で工場はいつ適切ですか?

は、工場は他のオブジェクトを作成するためのオブジェクト あります。コンストラクタの抽象化は であり、さまざまな 割り当てスキームの実装には を使用できます。

工場のクラスが必要で有益な場合は、誰でも説明してください。

私は現在、クラスを持ち、コンストラクタを使ってオブジェクト(duh!)を初期化するプロジェクトに取り組んでいますが、失敗して初期化されません。正しく作成されたかどうかを確認するSuccessプロパティがあります。これは、ファクトリクラスを実装するときの良い例ですか?この方法でCreate()メソッドはnullを返すことができ、Successプロパティを取り除くことができます。私は正しいアイデアを持っていますか?

+0

工場クラスまたは工場の方法ですか? – Bozho

答えて

7

ファクトリの状況に関する教科書の例は、インターフェイスといくつかの実装があるが、実装を公開したくない場合です。渡すパラメータに基づいて異なる実装のインスタンスを生成するファクトリ(メソッドまたはクラス)を実装します。ただし、インターフェイスタイプによって返されるため、呼び出し側は実装の詳細に負担をかけることはありません。

実際の例:ストリームリーダーのインターフェイスを定義し、ローカルファイル、ネットワークリソース、および標準入力から読み込む実装を定義したとします。その後、単一のパラメータ(URI)を取り、適切なリーダーを返すファクトリメソッドを記述します。呼び出し側は実装の詳細を知る必要はありません。最も重要な点は、データ:URIなどの別のインプットメソッドをサポートすることを決定した場合は、別の実装を追加してファクトリに追加するだけです。コールするコードで何も変更する必要はありません。

+0

...そして、OCPはすべてについてです:) Nice answer。 – Marcus

+0

ありがとう、それは意味をなさない – Marlon

3

工場が目的とするのは、実行時にプログラムがコンパイルされたときにデータが利用できない可能性があり、多くの場合のうちvirtualメソッドを持つ特定のクラスから派生したデータを取得する必要があります。あなたのプログラム。

多様な言語機能を使用すると、さまざまな種類のデータ(共通基本クラスを共有)で作業し、型に適した動作を呼び出すことができます。これを利用するには、異なる派生型のオブジェクトを持つ必要があります。 Comp Sciコースでこれについて学ぶときには、各Derivedタイプのいくつかの作成をハードコードし、基本クラスへのポインタを使って演奏します。複雑な現実世界の問題では、ハードコーディング作成ではなく、データベーステーブル、ファイル、ソケットなど、プログラムの入力から到着したデータから逃れることがよくあります。それぞれの点で正確に何が表示されているかに応じて、適切に型付けされたオブジェクトを作成してそれを表現したいと思うかもしれませんが、おそらく、コンパイル時の既知の型、つまり基本クラスへのポインタを使用して、次に、基底クラスが約束している操作を実行できるだけでなく、派生クラスの実装への動的ディスパッチが必要な場合もありますが、必要に応じて、実際のデータ型が何であるかを正確に判断し、 。

struct Animal 
{ 
    Animal(const std::string& name) : name_(name) { } 

    virtual void eat_from(Supplies&) = 0; // animals must do in their own way... 
    virtual bool can_jump() { return false; } // some animals might, assume not... 

    std::string name_; 
}; 

struct Elephant : Animal 
{ 
    Elephant(const std::string& name, const std::string& partner) 
     : Animal(name), partner_(partner) 
    { } 

    std::string partner_; 

    virtual void eat_from(Supplies&) { supplies.consume(Tofu, 10 * kg); } 
    void swing_trunk(); // something specific to elephants 
}; 

struct Mule : Animal 
{ 
    Mule(const std::string& name, double kgs) : Animal(name), kilograms_(kgs) { } 
    double kilograms_; 

    virtual void eat_from(Supplies&) { supplies.consume(Grass, 2 * kg); } 
    virtual bool can_jump() { return true; } 
}; 
:あなたはこれらを表現するために次のクラス階層構造を持っている

elephant name Tip-Toes partner Mega 
mule name Dare-You mane_length 132 

:たとえば

は、あなたがそれぞれに異なる種類のデータを収集した方法を示し、次のファイルを、読んで言います

ファクトリメソッドの仕事は、ゾウをミュールと区別して、適切なタイプの新しいオブジェクトを返すことです(これは、単にAnimalではありません)。

Animal* factory(std::istringstream& input) 
{ 
    std::string what, name; 
    if (input >> what && input >> name) 
    { 
     if (what == "elephant") 
     { 
      std::string partner; 
      if (input >> partner) 
       return new Elephant(name, partner); 
     } 
     else if (what == "mule") 
     { 
      double mane_length; 
      if (input >> mane_length) 
       return new Mule(name, mane_length); 
     } 
    } 
    // can only reach here on unparsable input... 
    throw runtime_error("can't parse input"); 
} 

あなたはその後、動物* Sを保存し、それらの操作を実行できます。

あなたの質問に戻って
std::vector<Animal*> animals; 
// we expect 30 animals... 
for (int i = 0; i < 30; ++i) animals.push_back(factory(std::cin)); 

// do things to each animal... 
for (int i = 0; i < 30; ++i) 
{ 
    Animal* p_unknown = animals[i]; 

    std::cout << p_unknown->name() << '\n'; 

    if (Elephant* p = dynamic_cast<Elephant*>(p_unknown)) 
     p->swing_trunk(); 
} 

私は現在、私はクラスを持っており、使用するプロジェクトに取り組んでいますオブジェクトを初期化するコンストラクタですが、失敗して初期化されません。正しく作成されたかどうかを確認するSuccessプロパティがあります。これは、ファクトリクラスを実装するときの良い例ですか?この方法でCreate()メソッドはnullを返すことができ、Successプロパティを取り除くことができます。私は正しいアイデアを持っていますか?

いいえ、まだ1つのタイプしか含まれていないため、工場が便利な状況ではありません。あなたは(OOの意味で)持っているものに固執するだけですが、例外をスローしたり、プログラムを中止したりすることができます。

+0

素敵な答えは、ファクタメソッドとファクトリクラスの違いはありますか? –

+1

"メソッド"は単に "関数"の別の名前であり、明らかに必要な関数やデータをグループ化するためのクラスに入れておくことができます。うまく機能しています。ファクトリ関数(および/またはサポートコード)が 'virtual'になり、いくつかのファクトリ実装のどれかが呼び出されるかもしれないことを意味する、抽象的なファクトリクラスを持つときには、もっと面白く、たとえば、「XML-parsing」と「JSON-parsing」の2つのファクトリを選択してから使用してください。 –

+0

Thanks Tony。コメントはオレンジ色ですか? –

1

ファクトリパターンについての私の意見は、やや異なっていますが、とにかく私はそれを与えます。

工場は、に関連するタイプのセットを作成するための工場です。関連するということは、必ずしも同じインターフェイスを必ずしも実装するという意味ではありません。関連することは、インスタンス化する必要がある1つのタイプがあり、次に別のオブジェクトを作成する必要があり、2番目のオブジェクトの具象(実装)タイプが最初のオブジェクトの具体的なタイプに依存するということです。

1つのタイプのオブジェクトのみを作成するファクトリ(「関連性」はありません)は、ファクトリではなくストラテジーに似ています。

上記の定義は、工場があなたが思っているほどには使用されていないか、必要ではないことを意味します。

オブジェクトの初期化に失敗した場合は、ステータスフィールドに頼るのではなく、例外をスローしてフェイルファーストの方法をお勧めします。

+0

ファクトリパターンの重要なアイデアは、統一された作成メカニズムを提供することによって、同様の機能の異なる実装を隠すことです。関連性を表現するためにインターフェイス、抽象基本クラス、関数ポインタ、またはダックタイピングを使用するかどうかは、比較的重要ではなく、プログラミング言語と個人の好みによって異なります。 – tdammers

+0

"関連することは、あるタイプのインスタンス化が必要な場合に、別のオブジェクトを作成する必要があり、2番目のオブジェクトの具象(実装)タイプが最初のオブジェクトの具体的なタイプに依存するということです。それはまったく正しいことではありません。クローン関数は、作成できるオブジェクトの種類が1つだけであるにもかかわらず、ファクトリの定義を満たします。より重要なのは、ファクトリのサブセットであり、単純なケースよりもはるかに限定的で複雑な抽象的なファクトリを記述することです。 –

1

私はWikipediaから単純な答え:)

をしようとします。オブジェクトの作成が大幅にコードを複製せずに再利用を排除する

  • :とき

    は、工場出荷時のパターンを使用してください。

  • オブジェクトを作成するには、作成対象オブジェクトに含まれていない情報やリソースにアクセスする必要があります。
  • 一貫した動作を保証するために、作成されたオブジェクトのライフタイム管理を集中化する必要があります。

私はあなたが特定のケースでは工場を必要としないと思います。 しかし、私は1つが傷ついていないと言うでしょう。

インターフェイスを返して実装クラスを非表示にする場合は、工場でよく使用されます。例えば

public final class LoginFactory { 

private final static Map<SomeEnum, LoginInterface> IMPLEMENTERS = new HashMap<SomeEnum, LoginInterface>(); 

static { 
    IMPLEMENTERS.put(SomeEnum.QUICK, new QuickerLoginImpl()); 
    IMPLEMENTERS.put(SomeEnum.SECURE, new SecureLoginImpl()); 
} 

public static LoginInterface getLoginImpl(SomeEnum type) { // my naming is bad ... 
    return IMPLEMENTERS.get(type); 
} 

}

この方法であなたも気付いあなたSecureLoginImplあなたのAPIのユーザーのない例えばMoreSecureLoginImplに変更することができます。

このwikiページをご覧になることもできます。

+0

例がJava btwにあります。申し訳ありませんが...必要な場合は、他にも例を挙げることができます。 – Simeon

+0

申し訳ありません:)私は質問に言語を記載する必要があります – Marlon

+0

あなたはC + +でそれが必要ですか? – Simeon

関連する問題