14

ファクトリ パターンを理解しようとしています。多くの実装がある場合、ファクトリ パターンには多数の if else または switch ケースがあります。また、新しい実装を導入するたびに、工場コードを変更する必要があります

以下の例のように、犬のアヒルが明日のように Pet インターフェースを実装していると仮定すると、多くの動物がペット インターフェースを実装している場合、私の工場は多くの if else if コードまたはスイッチ ケースで長く悩まされます。より動的なアプローチを導入してこれを解決する方法はありますか?

package com.javapapers.sample.designpattern.factorymethod;

//Factory method pattern implementation that instantiates objects based on logic
public class PetFactory {

    public Pet getPet(String petType) {
        Pet pet = null;

        // based on logic factory instantiates an object
        if ("bark".equals(petType))
            pet = new Dog();
        else if ("quack".equals(petType))
            pet = new Duck();
        return pet;
    }

動物が増えたら

if ("bark".equals(petType))
    pet = new Dog();
else if ("quack".equals(petType))
    pet = new Duck();
else if ("mno".equals(petType))
    pet = new MNO();
else if ("jkl".equals(petType))
    pet = new JKL();
else if ("ghi".equals(petType))
    pet = new GHI();
else if ("def".equals(petType))
    pet = new DEF();
......
else if ("abc".equals(petType))
    pet = new ABC();
return pet
4

5 に答える 5

23

動的なアプローチがあると思います:

  1. あなたの工場では、Map<String, Class<? extends Pet>>
  2. Pet を拡張するすべてのクラスの静的コンストラクターで、そのようなマップに登録します。
  3. クラスを作成するよりもmap.get(pet).newInstance(もちろん、nullをチェックする必要があります)
于 2013-08-14T15:46:56.210 に答える
11

ファクトリ パターンの背後にある考え方は、設計時に型が必ずしもわからないオブジェクトを動的にインスタンス化できるようにすることです。

大きなifブロックを持つことは、その目的を無効にします。

このパターンを実装する効果的な方法は、基本ファクトリ インターフェイスを実装し、その型の新しいオブジェクトをインスタンス化する機能を備えた各型のファクトリも用意することです (ちなみに、Java では、ビルトインClassは一例です)。そのような工場の)。

次に、names/ids/etc のマップを登録します。実行時にこれらの個々のファクトリのインスタンスに。タイプの 1 つをインスタンス化するときは、マップ内のファクトリを名前で検索し、それを使用してそのタイプの新しいオブジェクトをインスタンス化します。

個々の工場をマップに登録する方法は完全に未定です。一部を明示的に登録したり、構成ファイルをスキャンしたりできます。

if基本的に、実行時に動的に作成されるマップでブロックを置き換えたいと考えています。

事前に登録された「マップ」を単独で使用する必要さえありません。特定の名前のオブジェクトをオンザフライで作成する方法、またはその 2 つの組み合わせを作成する方法を理解することが適切な場合があります (たとえばClass.forName()、すでにロードされているクラスが見つかりません)。要点は、名前のクラス型への変換は、基本ファクトリがクラス型が何であるかを実際に知らなくても発生する可能性があるということです。

Java リフレクションは、すでにClass.forName()and/orを介して非常に実行可能なファクトリ実装を提供していることに注意してくださいClass.newInstance()

于 2013-08-14T15:44:00.640 に答える
4

リフレクションを使用する

public Pet getPet(String petType)
{
     Pet _pet = (Pet)Class.forName(petType).newInstance();
     return _pet;
}

引数を「bark」、「quack」から「Dog」、「Duck」などに変更する必要があります

于 2013-08-14T15:55:12.933 に答える
1

私は同様の問題を抱えていたので、これについて少し頭を悩ませていましたが、最終的にReflections Libraryに基づく解決策にたどり着きました(Reflectionsの最後のSに注意してください!)

たとえば、すべてのペットのサブクラスにそれらを区別するために使用できる属性がある場合、問題に適用できます。

   public String petType;

工場のメソッドは次のようになります。

        public static Pet getPet(String _petType) {
    String packageName = "your.package.with.pet.classes";

    Reflections reflections = new Reflections(packageName);

    Set<Class<? extends Pet>> allPets = reflections
            .getSubTypesOf(Pet.class);

    Iterator<Class<? extends Pet>> it = allPets.iterator();

    while (it.hasNext()) {
        try {
            Pet pet = it.next().newInstance();
            if (pet.petType.equals(_petType))
                return pet;
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    System.out.println("Pet " + _petType
            + " not yet implemented in package " + packageName);
    return null;
}

新しいペットが定義された場合、このメソッドは影響を受けません。

長所:

  • Pet サブクラスをさらに変更する必要はなく、Factory によって維持される Map 構造の初期化/登録も必要ありません。

  • クラス名の代わりにいくつかの属性で Pet サブクラスを区別できるため、Java Reflection API に基づくソリューションよりも一般的です。

短所:

  • 適切なサブクラスを見つけるために、すべての Pet サブクラスのローカル インスタンスを作成します。この点では、このアプローチは改善できると確信しています。
于 2014-09-03T16:12:10.927 に答える