クラスの一意のインスタンスを確保する方法は?


14

特定のクラスの各インスタンスが一意に識別可能なインスタンスであることを保証するさまざまな方法を探しています。

たとえばName、フィールドを持つクラスがありますname。John Smithに初期化されたNameオブジェクトを取得したら、John Smithという名前でもname別のNameオブジェクトをインスタンス化することはできません。インスタンス化が行われる場合は、元のオブジェクトへの参照を返す必要があります。新しいオブジェクトより。

これを行う1つの方法はMap、現在のすべてのNameオブジェクトを保持する静的ファクトリーを使用することであり、ファクトリーは、John Smithを名前として持つオブジェクトが存在しないことを確認してから、Nameオブジェクト。

私の頭の外で考えることができる別の方法は、Nameクラスに静的なマップを持ち、渡された値がname別のオブジェクトで既に使用されている場合にコンストラクタを呼び出して例外をスローするときに、例外をスローすることを知っていますコンストラクターでは一般的に悪い考えです。

これを達成する他の方法はありますか?



5
静的工場- :あなたはより良い最初のものと一緒に行く必要があります
のRohitジャイナ

16
@MarcB opはシングルトンを必要としません。彼は同じクラスの多くのインスタンスを持っているかもしれませんが、これらのインスタンスは識別可能でなければなりません。

1
@MarcB私はシングルトンパターンを知っていますが、クラスのインスタンスが1つだけ可能であることを保証すると思いましたか?複数のインスタンス、異なる値が必要です。質問がそれを明確にしていない場合は申し訳ありません。編集:投稿する前に最初のコメントのみを見ました。
ピーナッツ

2
I'm aware that one way of doing this is to have a static factory that holds a Map...では、なぜこのようにしたくないのですか?
FrustratedWithFormsDesigner

回答:


13

実際、あなたはすでにあなたの質問に答えています。ここでは、最初の方法がより効果的です。使用できると思う場所static factoryよりconstructorも常に使用することをお勧めします。そのためConstructor、この場合は使用しないようにすることができthrow some exceptionます。指定した名前のインスタンスが既に存在する場合は、そうする必要があります。

そのため、静的ファクトリーメソッドを作成できます。- getInstanceWithName(name)既に使用可能なインスタンスをその名前で取得し、存在しない場合は、新しいインスタンスを作成し、constructorプライベートを作成します。static factories

また、そのためには、クラス内で静的ListまたはMap作成されたすべての一意のインスタンスを維持する必要がありますFactory

編集:-

確かに-Effective Java-Item#1:コンストラクターの静的ファクトリーを検討する必要があります。その本よりも良い説明は得られません。


1
それはOPがすでに考えていることです。
BGurung

リンクの+1。OPの懸念のいくつかに明確に対処しているようです。
ロバートハーベイ

@RobertHarvey。ええ、その本はこれらのようなトピックのほとんどの最良の情報源です。そして、それが実際に最初のアイテムです。:)
Rohit Jain

効果的なJavaの+1、私は実際に私のバッグに座って本を持っています:)しかし、私は静的ファクトリー/静的メソッドとコンストラクターの例外とは別に、一意性を達成する他の方法について興味がありました。ない場合は、これを受け入れます。
ピーナッツ

@落花生。承知しました。これは、詳細な情報を得るために掘り下げるのに最適なリソースです。
ロヒトジャイン

5

効果的なJavaの言及は多くの信頼性を追加するように思われるので、この回答は次の点に基づいています。

  • 有効なJava項目8:等しいをオーバーライドする場合は、一般的な契約に従います
  • 効果的なJavaアイテム9:等しいをオーバーライドするときは常にhashCodeをオーバーライドする
  • 効果的なJavaアイテム15:可変性の最小化

私は一歩下がって、この名前オブジェクトのインスタンスが複数ある場合、どうして気にするのか疑問に思うでしょう。

この種のオブジェクトプーリングを行う必要はほとんどありません。OPはこれを行っているので、彼らは単純にNameオブジェクトをます==。または、キーのName内部HashMapまたは類似のオブジェクトを使用します。

もしそうなら、これは解決できるものです 適切な実装ですequals()

そのようです:

public final class Name {
  private final String name;

  public Name(String name) {
    if (name == null) {
      name = ""; //or exception if you like
    }
    this.name = name;
  }

  public String getName() {
    return name;
  }

  @Override
  public boolean equals(Object o) {
    if (!(o instanceof Name)) {
      return false;
    }
    Name other = (Name) o;
    return other.name.equals(name);
  }

  @Override
  public int hashCode() {
    return name.hashCode();
  }
}

完了したら、次のことが当てはまります。

Name a = new Name("weston");
Name b = new Name("weston");
assert(a.equals(b)); //same but:
assert(a!=b); //not the same instance
//test out the Name instances in a hashmap:
HashMap<Name,Object> map = new HashMap<Name,Object>();
Object detailsIn = new Object();
map.put(a,detailsIn);
Object detailsOut = map.get(b);
assert(detailsIn==detailsOut); //the map returned the same details object
//even though we put with `a` and got with `b` thanks to our correct equals implementation

私はあなたの目標を推測していますが、この方法Nameではハッシュマップなどでクラスを使用できます。それらはまったく同じインスタンスである必要ありませ


例、お願いします。
ロバートハーベイ

適切な実装の@RobertHarveyの例?
ウェストン

提供したようです。しかし、静的ファクトリメソッドでNameプロパティが等しいかどうかを単にチェックすることと、これがどのように異なるかは明確ではありません。
ロバートハーベイ

1
@RobertHarvey私はもっと説明しようとしましたが、静的ファクトリをまったく必要としない完全な代替手段を提供しています。名前ごとに複数のインスタンスを持ちたくないというOPの出発点に挑戦しています。
ウェストン

一意のオブジェクトを必要とする正当な理由は、同期ブロックでオブジェクトをモニターとして使用する場合です。互いに.equals()する2つのオブジェクトは機能しません。
リーコールドウェル

3
  • Nameインターフェースを作る
  • インターフェースを作成する NameFactoryメソッドをしてをName getByName(String)
  • 実装を作成NameFactoryAとしMap<String,WeakReference<Name>>、その中
  • synchronizegetByName新しいインスタンスを作成する前に、メソッド内の名前でマップ上にName
  • 必要に応じて、Nameインターフェイスの静的プライベート実装を使用して、NameFactory

このアプローチにより、次のことが保証されます。

  • 常に1つのインスタンスのみがName存在し、
  • クラスNameに必要以上に長く留まっている場合、クラスに「リンガー」メモリリークはありません。
  • インターフェイスを使用するため、デザインはモックされたオブジェクトでテスト可能のままです。

throwオブジェクトを返す代わりに同じ名前が存在する場合、またはオブジェクトを返す場合、ファクトリメソッドでメモリリークは発生しませんnull
ロバートハーベイ

@RobertHarvey私は、実際にはリークではなく、正当なオブジェクト(いわゆる「リンガ」)であるリークを意味します。単純な静的マップは、その値オブジェクトが解放されるのを防ぎますが、弱参照のマップは、他のライブ参照が存在している間だけそれらをメモリに保持します。
dasblinkenlight

ああ、私はあなたが何を意味するかわかります。これは、a destructorが便利なまれな例の1つかもしれません。
ロバートハーヴェイ

1
複雑すぎる。@weston answerのように実行し、equalsをオーバーライドし、ファクトリに名前のコレクションを保持させることができます。
Tulainsコルドバ

@ user1598390できる限り単純にすることをお勧めしますが、単純にすることはできません。westonの「解決策」はOPの問題を解決せず(マップはありません)、名前のコレクションが残留メモリリークを作成します(はい、Javaにメモリリークがあります)。
-dasblinkenlight

1

コンストラクタをプライベートにgetNameInstance(String)し、同じような名前のオブジェクトが既に存在する場合(静的クラスのhastableに基づいて)のようなメソッドを作成し、その参照を返すか、プライベートコンストラクタを使用して新しいオブジェクトを作成して追加する必要がありますハッシュテーブルへ


質問の第3段落で私が言ったことを繰り返しました。私は別の方法を求めていました。
ピーナッツ

すみません、ポストマイニングの前に答えをチェックしたので、ほぼ同時に答えたと思います。実際、StackOverflownで回答しようとしましたが、移行されました。
HericDenis

1

フォローしてみてください。作成する各オブジェクトを追跡する必要があります。この目的のために、私はリストを使用しています。インスタンスを作成する前に事前チェックを適用できるように、クラスコンストラクターをプライベートにしました。

class UniqueName
    {
        private string Name;
        public int ID;
        private static int Count=0;
        static List<UniqueName> lt=new List<UniqueName>();

        private UniqueName(string Name)
        {
            this.Name = Name;
            ID = ++Count;
        }

        public static UniqueName GetUniqueueInstance(string Name)
        {
            foreach (UniqueName un in lt)
            {
                if ( string.Compare( un.Name,Name,StringComparison.InvariantCultureIgnoreCase)==0)
                    return un;
            }

            UniqueName temp=new UniqueName(Name);
            lt.Add(temp);
            return temp;
        }
    }

これはJavaではありませんか?擬似コードを作成しましたか?または、他の言語で?明示的に指定してください。
ロヒトジャイン

C#のように見えます。Akshay、以外のコレクションを学ぶ必要がありListます。これはに適していDictionary<string,UniqueName>ます。
ウェストン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.