Möglichkeiten, eindeutige Instanzen einer Klasse sicherzustellen?

14

Ich suche nach verschiedenen Möglichkeiten, um sicherzustellen, dass jede Instanz einer bestimmten Klasse eine eindeutig identifizierbare Instanz ist.

Zum Beispiel habe ich eine NameKlasse mit dem Feld name. Wenn ich ein NameObjekt habe, das mit nameJohn Smith initialisiert wurde, möchte ich nicht in der Lage sein, ein anderes NameObjekt auch mit dem Namen John Smith zu instanziieren, oder wenn eine Instanziierung stattfindet, möchte ich, dass ein Verweis auf das ursprüngliche Objekt eher zurückgegeben wird als ein neues Objekt.

Mir ist bewusst, dass eine Möglichkeit dazu darin besteht, eine statische Factory zu haben, die Mapalle aktuellen Namensobjekte enthält, und die Factory prüft, ob ein Objekt mit dem Namen John Smith noch nicht vorhanden ist, bevor sie einen Verweis auf a zurückgibt NameObjekt.

Eine andere Möglichkeit, die mir auf den ersten Blick einfällt, besteht darin, eine statische Map in der NameKlasse zu haben und beim Aufruf des Konstruktors eine Ausnahme auszulösen, wenn der übergebene Wert für namebereits in einem anderen Objekt verwendet wird. Allerdings sind mir Ausnahmen bekannt in einem Konstruktor ist in der Regel eine schlechte Idee .

Gibt es andere Möglichkeiten, dies zu erreichen?

Erdnuss
quelle
1
Sie wollen einen Singleton
5
Sie sollten besser mit der ersten gehen: - statische Fabrik
Rohit Jain
16
@MarcB op will keinen Singleton. er kann viele Instanzen derselben Klasse haben, aber diese Instanzen müssen identifizierbar sein.
1
@MarcB Mir ist das Singleton-Muster bekannt, aber ich dachte, dass nur eine Instanz einer Klasse möglich ist? Ich möchte mehrere Instanzen, unterschiedliche Werte. Entschuldigung, wenn die Frage das nicht klar macht. Bearbeiten: Nur den ersten Kommentar vor dem Posten gesehen.
Erdnuss
2
I'm aware that one way of doing this is to have a static factory that holds a Map...Warum willst du es nicht so machen?
FrustratedWithFormsDesigner

Antworten:

13

Eigentlich haben Sie Ihre Frage schon beantwortet. Ihr erster Weg sollte hier effektiver sein. Verwenden static factoryist immer vorzuziehen, constructorwo immer Sie denken, dass Sie können. Sie können Constructorin diesem Fall also vermeiden , etwas anderes zu verwenden, throw some exceptionwenn bereits eine Instanz mit dem angegebenen Namen vorhanden ist.

Sie können also eine statische Factory-Methode erstellen: - Mit getInstanceWithName(name)dieser Methode wird die bereits verfügbare Instanz mit diesem Namen abgerufen. Wenn sie nicht vorhanden ist, wird eine neue Instanz erstellt und constructorals private Instanz definiert, wie dies normalerweise bei der Arbeit mit dieser Instanz der Fall ist static factories.

Dazu müssen Sie in Ihrer Klasse eine statische Listoder Mapalle eindeutigen Instanzen verwalten, die erstellt wurden Factory.

EDIT : -

Sie sollten auf jeden Fall durchlaufen - Effektives Java - Punkt 1: Betrachten Sie statische Fabriken über Konstruktoren . Eine bessere Erklärung als dieses Buch gibt es nicht.

Rohit Jain
quelle
1
Daran hat das OP bereits gedacht.
BGurung
+1 für den Link, der einige der Bedenken des OP deutlich auszuräumen scheint.
Robert Harvey
@RobertHarvey. Ja, dieses Buch ist die beste Quelle für die meisten dieser Themen. Und das ist der allererste Punkt in der Tat. :)
Rohit Jain
+1 für Effective Java, ich habe das Buch, das jetzt tatsächlich in meiner Tasche sitzt :) Allerdings war ich nur neugierig auf andere Möglichkeiten, die Einzigartigkeit zu erreichen, abgesehen von der statischen Factory / statischen Methode und der Ausnahme im Konstruktor. Wenn es keine gibt, werde ich das akzeptieren.
Erdnuss
@Erdnuss. Sicher. Dies ist die beste Ressource, um mehr Informationen zu erhalten.
Rohit Jain
5

Erwähnungen von effektivem Java scheinen eine Menge Glaubwürdigkeit zu verleihen, daher stützt sich diese Antwort auf:

  • Wirksames Java Punkt 8: Befolgen Sie den allgemeinen Vertrag, wenn Sie gleichwertig sind
  • Effektives Java-Element 9: Überschreiben Sie immer den hashCode, wenn Sie equals überschreiben
  • Effektives Java Punkt 15: Minimieren Sie die Wandlungsfähigkeit

Ich würde einen Schritt zurücktreten und fragen, warum es Sie interessiert, wenn es mehr als eine Instanz dieses Namensobjekts gibt.

Ich muss diese Art von Objektpooling selten durchführen. Ich vermute, dass das OP dies tut, damit sie ihre NameObjekte einfach vergleichen können ==. Oder verwenden Sie die NameObjekte in a HashMapoder ähnlichem als Schlüssel.

Wenn ja, ist dies etwas, das durch die ordnungsgemäße Implementierung vonequals() gelöst werden kann .

Wie so:

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();
  }
}

Sobald dies erledigt ist, gilt Folgendes:

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

Ich vermute Ihr Ziel, aber auf diese Weise können Sie die NameKlasse in Hash-Maps usw. verwenden, und sie müssen nicht genau die gleiche Instanz sein.

Weston
quelle
Beispiel, bitte.
Robert Harvey
@RobertHarvey Beispiel für eine ordnungsgemäße Implementierung?
Weston
Sieht so aus, als hätten Sie eine bereitgestellt. Ich bin mir jedoch nicht sicher, inwiefern dies anders ist als das bloße Überprüfen der Name-Eigenschaft auf Gleichheit in einer statischen Factory-Methode.
Robert Harvey
1
@RobertHarvey Ich habe versucht, mehr zu erklären, ich biete eine vollständige Alternative an, die überhaupt keine statische Fabrik erfordert, und ich fordere den Ausgangspunkt der OPs heraus, dass sie nicht mehr als eine Instanz pro Name haben wollen.
Weston
Ein gültiger Grund für die Anforderung eindeutiger Objekte besteht darin, dass ein synchronisierter Block das Objekt als Monitor verwenden soll. Zwei Objekte, die .equals () sind, funktionieren nicht.
Leigh Caldwell
3
  • Machen Sie Nameeine Schnittstelle
  • Erstellen Sie eine Schnittstelle NameFactorymit einer MethodeName getByName(String)
  • Erstellen Sie eine Implementierung von NameFactorymit einem Map<String,WeakReference<Name>>darin
  • synchronizeauf der Karte nach Namen innerhalb der getByNameMethode, bevor Sie neue Instanzen von erstellenName
  • Verwenden Sie optional eine statische private Implementierung der NameSchnittstelle in Ihrer Implementierung vonNameFactory

Mit diesem Ansatz können Sie Folgendes sicherstellen:

  • Es ist immer nur eine einzige Instanz von Namevorhanden.
  • Es gibt kein "Lingerer" -Speicherverlust in Ihrer Klasse, wenn Sie Namelänger bleiben als nötig.
  • Das Design bleibt mit verspotteten Objekten testbar, da Sie Schnittstellen verwenden.
dasblinkenlight
quelle
Sie haben keine Speicherlecks mit einem Factory - Methode entweder, wenn Sie , throwwenn der gleiche Name anstelle der Rücksendung ein Objekt vorhanden ist , oder wenn Sie eine Rückkehr nullObjekt.
Robert Harvey
@RobertHarvey Ich meine die Lecks, die nicht wirklich Lecks sind, sondern legitime Objekte (die sogenannten "Lingerer"). Eine einfache statische Karte würde verhindern, dass ihre Wertobjekte freigegeben werden, während eine Karte mit schwachen Referenzen sie nur so lange im Speicher hält, wie andere Live-Referenzen existieren.
dasblinkenlight
Ah, ich verstehe, was du meinst. Dies könnte einer der seltenen Fälle sein, in denen a destructornützlich wäre.
Robert Harvey
1
Zu komplex Dies kann wie bei der @weston-Antwort geschehen, es kann gleich überschrieben werden und die Fabrik kann eine Sammlung von Namen führen.
Tulains Córdova
@ user1598390 Man sollte das Ding so einfach wie möglich machen, aber nicht einfacher. Die "Lösung" von weston löst das OP-Problem nicht (es gibt keine Karte), und eine Sammlung von Namen führt zu einem längeren Speicherverlust (ja, es gibt Speicherverluste in Java).
dasblinkenlight
1

Sie sollten die Konstruktoren als privat getNameInstance(String)kennzeichnen und Methoden erstellen, z. B. wenn bereits ein Objekt mit demselben Namen vorhanden ist (basierend auf einer statischen Klasse, die Sie hastabilisieren können), geben Sie diesen Verweis zurück. Andernfalls erstellen Sie ein neues Objekt mit Ihrem privaten Konstruktor und fügen es hinzu zur Hashtabelle

HericDenis
quelle
Sie haben wiederholt, was ich im 3. Absatz der Frage gesagt habe. Ich habe nach alternativen Wegen gesucht.
Erdnuss
Es tut mir leid, ich glaube, wir haben fast zur gleichen Zeit geantwortet, weil ich die Antworten überprüft habe, bevor ich meine postete. Eigentlich habe ich versucht, es auf StackOverflown zu beantworten und dann wurde es migriert.
HericDenis
1

Versuchen Sie folgendes. Sie müssen jedes Objekt, das Sie erstellen, im Auge behalten. Zu diesem Zweck benutze ich List. Und den Klassenkonstruktor privat gemacht, damit die Vorabprüfung vor dem Erstellen einer Instanz angewendet werden kann

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;
        }
    }
Akshay
quelle
Das ist nicht Java? Hast du einen Pseudocode geschrieben? Oder in einer anderen Sprache? Bitte geben Sie dies explizit an.
Rohit Jain
Sieht aus wie C #. Akshay, du solltest andere Sammlungen lernen als List. Dies wäre eine gute Passform für Dictionary<string,UniqueName>.
Weston