オブジェクトの重複を避けるための静的ファクトリメソッド

StackOverflow https://stackoverflow.com/questions/1416763

  •  06-07-2019
  •  | 
  •  

質問

Joshua Blochの" 効果的なJavaプログラミング言語ガイド&quotを読んでいた;。
彼は、静的なファクトリメソッドを使用して、不要な重複オブジェクトを回避できると説明しています。
私はこれをよく理解していません。
誰でも説明できますか?

役に立ちましたか?

解決

実際の例:

Javaは、バイトを表すプリミティブ型とオブジェクト型の両方をサポートしています。プリミティブをオブジェクトに変換するとき、次のようなことができます:

Byte b = new Byte( (byte) 65);

これにより、呼び出しごとに新しいインスタンスが作成されます。代わりに:

Byte b = Byte.valueOf( (byte) 65);

すべての呼び出しで、メソッドvalueOf()は、バイト値65を表すByteオブジェクトの同じインスタンスを返します。

10000の呼び出し後、1番目の例では10000個のオブジェクトが作成されますが、2番目の例では、Byteクラスには-128から127までのすべての数値を表すByteオブジェクトの内部キャッシュがあります。

他のヒント

非複製に関するすべての回答は、シングルトンパターンに焦点を当てているようです。これは、非複製の良い例ですが、一般的な場合に使用するのは悪いパターンです。私の考えでは、与えられたアプリケーションにはゼロから1つのシングルトンがあり、ゼロが優先されます。ただし、不要なオブジェクトを作成しないこととはほとんど関係ありません。

代わりに、多くのDateオブジェクトを作成する必要があるアプリケーションを検討してください。 Dateオブジェクトが非常に多く作成されているため、Dateオブジェクトの作成がパフォーマンスに悪影響を及ぼしています。そのため、Dateオブジェクトのコンストラクターを呼び出す代わりに、コードはリファクタリングされ、ファクトリメソッドを介してのみDatesを作成します。このファクトリメソッド内で、要求された日付が既に作成されているかどうかを確認するためにマップがチェックされます。もしそうであれば、同じオブジェクトがマップから返されます。それ以外の場合は、新しいものが作成され、マップに入れられて返されます。

紛らわしいと思われるのは、ファクトリメソッドを呼び出して、重複オブジェクトの作成を防ぐ方法です。ファクトリメソッドを呼び出すだけでは、実際には何も変わりません。ファクトリーの呼び出しで許可されるのは、コードが引き継ぎ、オブジェクトの作成に関する決定を下すことです。 newを呼び出すとき、そのような決定はできません。

この質問も参照してくださいパターンとその用途についての洞察。

コンストラクターを呼び出すと、コンストラクターは常に新しいオブジェクトを返します(例外がスローされない限り)。静的なファクトリメソッド、またはそれに関するあらゆる種類のファクトリは、常に新しいオブジェクトを返す必要はありません。たとえば、従来のシングルトンデザインパターンの getInstance()メソッドは、常にまったく同じオブジェクトを返すファクトリメソッドです。オブジェクトを強制するために一度だけインスタンス化できるか、何らかのオブジェクトプールを作成するかなど、この種のことをしたい場合があります。静的ファクトリメソッド。主な目的は、適切な名前の擬似コンストラクタを作成することです。

これは、静的ファクトリーメソッドを使用して適切な名前の擬似コンストラクターを作成する(やや馬鹿げた)例です。このクラスを検討してください:

class Person {

   public Person(Role role) {
      setRole(role);
   }

   ...
}

静的ファクトリメソッドを使用せずに、次のような操作を実行できます。

Person employee = new Person(Role.EMPLOYEE);
Person manager = new Person(Role.MANAGER);

代わりに、静的ファクトリメソッドを作成できます:

class Person {

   public static Person newEmployee() {
      return new Person(Role.EMPLOYEE);
   }

   public static Person newManager() {
      return new Person(Role.MANAGER);
   }

   private Person(Role role) {
      setRole(role);
   }

   ...
}

そして代わりにこのようなことをするかもしれません:

Person employee = Person.newEmployee();
Person manager = Person.newManager();

これは良い例ではないかもしれませんが、より複雑なコンストラクター、または説明の少ないパラメーターを持つコンストラクターを検討してください。ファクトリメソッドルートを使用すると、コードがより明確になる場合があります。もちろん欠点もあります...

オブジェクトの作成を制限する限り、CEOが複数になることは決してないような奇妙な制約を考慮してください。

class Person {

   private static Person singletonCEO = new Person(Role.CEO);

   public static Person newCEO() {
      return singletonCEO;
   }

   ...
}

および作成方法:

Person ceo1 = Person.newCEO();
Person ceo2 = Person.newCEO();

assertThat(ceo1, is(ceo2)); // JUnit 4.x

これらの例がお役に立てば幸いです。

ファクトリーメソッドパターンは、作成する必要がない場合に役立ちます。何らかのアクションを実行するためのオブジェクトの新しいインスタンス。

ここに、同じオブジェクトを返す静的ファクトリー・メソッドが役立つ場所として考えられる一般的なケースをいくつか示します。

  1. オブジェクトの作成にはコストがかかります-オブジェクトがインスタンス化されると多くの処理が行われるため、オブジェクトを複数回インスタンス化することは望ましくありません。 (これはシングルトンパターンにも関連しています。)

  2. オブジェクトは状態を保持しません-インスタンス間で状態に違いがない場合、毎回新しいオブジェクトを作成する良い目的はありません。

ファクトリメソッドパターンのウィキペディアページには、このトピックに関する詳細が記載されています。


具体的な例を見てみましょう。

DateFormat クラスは < code> getInstance 静的メソッドは DateFormat インスタンスを返します。このインスタンスを使用して、 Date をロケールに応じて事前設定された書式にフォーマットできます。マシン。

返される DateFormat は、すべての日付フォーマットアクションで同じフォーマットを使用しているため、毎回新しい DateFormat インスタンスを作成する理由はありません。

一般に、これが実装される方法は、インスタンスがまだ存在しない場合にインスタンスを作成し、そのインスタンスへの参照を保持することです。インスタンスが再び必要な場合、参照が返されます。 (これは、一般にシングルトンパターンの実装方法でもあります。)

例:

class MySingleInstanceObject {

  private MySingleInstanceObject instance;

  private MySingleInstanceObject() {
    // Initialize the object.
    // This may be expensive.
  }

  public MySingleInstanceObject getInstance() {
    if (instance == null) {
      instance = new MySingleInstanceObject();
    }

    return instance;
  }
}

(FYI、上記のコードはシングルトンの例です。また、スレッドセーフではありません。)

よく覚えていれば、彼は本の中で例を示しています。 Decimal を検討してください。ゼロは非常に頻繁に使用されます。したがって、静的ファクトリメソッド Decimal.valueOf(&quot; 0&quot;)を呼び出す場合(これが実際のAPIであるかどうかはわかりませんが、この例では重要ではありません) 0を表す10進数のインスタンスが返され、どの呼び出しでも同じインスタンスになります。実装は次のようになります。

public class Decimal {
    private static Decimal zero = new Decimal(0);

    public static Decimal valueOf(String s) {
        if (s.equals("0")) {
            return zero;
        } else {
            return new Decimal(parse(s)); // or whatever
        }

    // rest of the class
}

ゼロのインスタンスは1つしかありませんが、他の番号の場合は新しいオブジェクトが作成されます。また、これはファクトリメソッドで機能し、コンストラクタでこれを行うことはできません。前者の利点として、Blochが指摘しようとしていたことです。

そして、Yishaiが言及したように、それはシングルトンとそれほど密接に関連していません。ご覧のとおり、周囲にたくさんのDecimalオブジェクトを配置できます。代わりに、ファクトリメソッドを使用して、作成するインスタンスの数を完全に制御できます。それが工場と呼ばれる理由です。

この本の一部を読むことができましたここ彼が書いたものを読んだ後、彼が言っていることは、静的ファクトリーメソッドが開発者としてあなたにもっと柔軟性を与え、返されるものをより明確にすることもできるように思えます。これをコンストラクターと比較すると、コンストラクターは返されるものに関して明確性を提供しない場合があります。さらに、私は魅力的だと思った静的ファクトリーメソッドでキャッシングなどのことを行うことができます。このレベルの制御と柔軟性が必要な場合、このアプローチは良いアプローチのようです。

キャッシュを使用する場合は、不要な複製オブジェクトを作成しないという点が役立ちます。この静的ファクトリアプローチを使用すると、静的ファクトリメソッドの呼び出しごとに同じオブジェクトを返すことができます。

例:

public class Person
{

   private Person(string firstName, string lastName)
   {
      this.FirstName = firstName;
      this.LastName = lastName;
   }

   public string FirstName {get; private set;}
   public string LastName {get; private set;}

   private static Dictionary<string, Person> objectPool = new Dictionary<string, Person>();
   private object lockObject = new object();

   public static Person CreatePerson(string firstName, string lastName)
   {
      var result = objectPool[firstName + lastName];
      Person person = null; 
      if (result != null)
      {
         return result
      }
      lock(lockObject)
      {
          person = new Person(firstName, lastName);
          objectPool.Add(firstName + lastName, person)
      }
      return person;
   }
}

オブジェクトのインスタンス化を作成するファクトリクラスがある場合、オブジェクトを作成するたびに、ファクトリクラスもインスタンス化する必要があります。基本的に、このファクトリクラスの複製を作成します。

静的な場合、使用するファクトリのインスタンスは1つだけです。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top