web-dev-qa-db-ja.com

工場クラス

オブジェクトを直接インスタンス化する方がはるかに便利だと思われるため、個人的にはファクトリクラスのアイデアを理解していません。私の質問は簡単です。どのような状況で、ファクトリクラスパターンを使用するのが最良の選択肢であり、どのような理由で、良いファクトリクラスはどのように見えるのでしょうか。

51
Neil Locketz

ここでの考え方は、関心事の分離です。オブジェクトを使用するコードにも、インスタンス化するのに十分な情報がある場合、ファクトリーは必要ありません。ただし、APIユーザーに考えてはいけない(または混乱させたくない)何らかのロジックまたは構成が関係している場合は、それらをすべてファクトリーで非表示(および再利用のためにカプセル化)できます。

次に例を示します。GoogleApp Engineが提供するサービスの1つにアクセスしたい場合。同じコードが、開発環境(マスタースレーブと高可用性の2つのバージョンがある)と完全に異なるローカル開発環境の両方で動作するはずです。 Googleは、内部インフラストラクチャの内部動作についてユーザーに伝えたくないので、知りたくありません。そのため、インターフェイスとファクトリー(および、これらのインターフェイスを実装するためのいくつかの実装を提供します).

26
Thilo

これが私のコードベースからの実際のライブファクトリです。あるデータセットからデータをサンプリングする方法を知っているサンプラークラスを生成するために使用されます(元々はC#にあるため、任意のJava faux-pas)

class SamplerFactory
{
  private static Hashtable<SamplingType, ISampler> samplers;

  static
  {
    samplers = new Hashtable<SamplingType, ISampler>();
    samplers.put(SamplingType.Scalar, new ScalarSampler());
    samplers.put(SamplingType.Vector, new VectorSampler());
    samplers.put(SamplingType.Array, new ArraySampler());
  }

  public static ISampler GetSampler(SamplingType samplingType)
  {
    if (!samplers.containsKey(samplingType))
      throw new IllegalArgumentException("Invalid sampling type or sampler not initialized");
    return samplers.get(samplingType);
  }
}

以下に使用例を示します。

ISampler sampler = SamplerFactory.GetSampler(SamplingType.Array);
dataSet = sampler.Sample(dataSet);

ご覧のとおり、コードはそれほど多くありません。

ArraySampler sampler = new ArraySampler();
dataSet = sampler.Sample(dataSet);

工場を使用するより。それで、なぜ私も気にしますか?さて、2つの基本的な理由があります。それはお互いに基づいています:

  1. まず、コードのシンプルさと保守性です。呼び出し元のコードで、enumがパラメーターとして提供されているとしましょう。つまりサンプリングを含むデータを処理する必要があるメソッドがある場合、次のように記述できます。

    void ProcessData(Object dataSet, SamplingType sampling)
    {
      //do something with data
      ISampler sampler = SamplerFactory.GetSampler(sampling);
      dataSet= sampler.Sample(dataSet);
      //do something other with data
    }
    

    次のようなより面倒な構造の代わりに:

    void ProcessData(Object dataSet, SamplingType sampling)
    {
      //do something with data
      ISampler sampler;
      switch (sampling) {
        case SamplingType.Scalar:  
          sampler= new ScalarSampler();
          break;
        case SamplingType.Vector:  
          sampler= new VectorSampler();
          break;
        case SamplingType.Array:
          sampler= new ArraySampler();
          break;
        default:
          throw new IllegalArgumentException("Invalid sampling type");
      }
      dataSet= sampler.Sample(dataSet);
      //do something other with data
    }
    

    この怪物は、サンプリングを必要とするたびに書かなければならないことに注意してください。そして、たとえば、ScalarSamplerコンストラクターにパラメーターを追加した場合、または新しいSamplingTypeを追加した場合、変更するのがどれほど楽しいか想像できます。そして、このファクトリには現在3つのオプションしかありません。20の実装を持つファクトリを想像してください。

  2. 第二に、それはコードの分離です。ファクトリを使用する場合、呼び出し元のコードは、ArraySamplerというクラスが存在することすら知らないか、知る必要がありません。クラスは実行時に解決することもでき、呼び出しサイトは賢明ではありません。したがって、結果として、クラスを完全に削除することを含むがこれに限定されないArraySamplerクラスを自由に変更できます。配列データにもScalarSamplerを使用することにしました。行を変更するだけです

    samplers.put(SamplingType.Array, new ArraySampler());
    

    samplers.put(SamplingType.Array, new ScalarSampler());
    

    そしてそれは魔法のように動作します。呼び出し元のクラスのコードを1行変更する必要はありません。これは数百に及ぶ場合があります。事実上、工場はサンプリングの内容と方法を制御し、サンプリングの変更は、システムの他の部分と接続された単一のファクトリクラス内に効率的にカプセル化されます。

51
SWeko

個人的には、実行時にインターフェイスの実装が不明な場合、または動的にできる場合は、ファクトリパターンを使用します。

つまり、開発者として、オブジェクトのインスタンスへの既知のインターフェイスに対して作業を行いますが、実装がどのように機能するかは気にしません。

例を挙げましょう。ファクトリパターンを使用して、データベースのオブジェクトを提供できます。そのデータベースがフラットファイル、ローカル/シングルユーザーデータベース、サーバーデータベース、またはWebリソースであるかどうかは気にせず、ファクトリがそれらのオブジェクトを生成および管理できることだけです。

私はそれらの各ケースの実装を書かなければならないのは嫌だ:P

4
MadProgrammer

Effective Java Joshua Blochの本、私によって部分的に書き直されたもの:

1)静的フ​​ァクトリメソッド([〜#〜] sfm [〜#〜])は、コンストラクターとは異なり、名前を持っています。

public static ComplexNumber one () {
    return new ComplexNumber(1, 0);
}

public static ComplexNumber imgOne () {
    return new ComplexNumber(0, 1);
}

public static ComplexNumber zero () {
    return new ComplexNumber(0, 0);
}

2)SFMが呼び出されるたびに新しいオブジェクトを作成する必要はありません

public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE; 
}

3)SFMは、戻り型の任意のサブタイプのオブジェクトを返すことができます。

4)SFMは、パラメーター化された型インスタンスの作成の冗長性を減らします。

public static <K, V> HashMap<K, V> newInstance() {
    return new HashMap<K, V>();
}

Map<String, List<String>> m = HashMap.newInstance();
4

Thiloの答えを補足するために、コンストラクタとしてブール値のみを持つオブジェクトがあると仮定しましょう。可能な値が2つしかないため、毎回構築するのはまったく無駄です。

この場合、静的ファクトリメソッドを作成できます。 JavaのBooleanクラスは例です:Boolean.valueOf()

1
fge

工場自体はそれほど簡単にその美しさを示しません。たとえば、デコレータパターンを使用する場合、オブジェクトを直接インスタンス化すると、コードにいくつかの追加のカップリングが追加される可能性があります。 OOP先生が言うように、カップリングは悪いです:)したがって、装飾されたオブジェクトをインスタンス化し、カップリングを増やしたくない場合は、ファクトリを使用できます。

wikipedia を参照できますが、ほとんどのデザインパターンの基本的な考え方は、保守性や再利用性を向上させるために抽象化を導入することです。ファクトリメソッドパターンも例外ではありません。コードから作成の複雑さを抽象化することです。

単純な場合、ファクトリパターンを使用する必要はないようです。単にnewで十分です。ただし、より柔軟性や機能が必要な場合は、このパターンが役立ちます。

たとえば、新しいインスタンスが必要な場合を除き、静的ファクトリvalueOf(boolean)は一般的にnew Bealean(boolean)よりも適切な選択肢です。不要なオブジェクトの作成を避けるためです。ファクトリメソッドパターンは、Virtual Constructorとも呼ばれます。知っているように、ポリモーフィズムはOOPの重要な機能の1つですが、コンストラクターをポリモーフィックにすることはできません。この欠点はファクトリメソッドパターンによって克服できます。

本質的に、オブジェクトを直接(通常はnew経由で)インスタンス化することはほとんど具体的ではありません実装、ファクトリメソッドパターンはvolatileimplementationby stableinterfaceinterface)、オブジェクト作成のロジックを何らかの抽象化の背後にプッシュして、メンテナンス性と再利用性の高いコードを確保します。

最後の言葉として、ファクトリメソッドパターンやその他のデザインパターンの利点を完全に理解するには、 データ抽象化 、ポリモーフィック抽象化、および [〜#〜]を含むOOPの本質を理解する必要がありますsolid [〜#〜] 原則。

0
Hui Zheng