Metodi per garantire istanze uniche di una classe?

14

Sto cercando diversi modi per garantire che ogni istanza di una determinata classe sia un'istanza identificabile in modo univoco.

Ad esempio, ho una classe Name con il campo name . Una volta che ho un oggetto Name con name inizializzato in John Smith, non voglio essere in grado di istanziare un oggetto Name diverso anche con il nome come John Smith, o se l'istanziazione ha luogo Voglio un riferimento all'oggetto originale da restituire anziché a un nuovo oggetto.

Sono consapevole che un modo per farlo è avere un factory statico che contiene un Map di tutti gli oggetti Name correnti e il factory controlla che un oggetto con John Smith come nome non sia già esistito prima restituire un riferimento a un oggetto Name .

Un altro modo in cui mi viene in mente è avere una mappa statica nella classe Name e quando il costruttore è chiamato a lanciare un'eccezione se il valore passato per name è già in uso in un'altra oggetto, tuttavia sono consapevole lanciare eccezioni in un costruttore è generalmente una cattiva idea .

Ci sono altri modi per raggiungere questo obiettivo?

    
posta Peanut 22.10.2012 - 19:44
fonte

5 risposte

13

In realtà hai già risposto alla tua domanda. Il tuo primo modo dovrebbe essere più efficace qui. L'utilizzo di static factory è sempre preferibile rispetto a constructor ovunque tu pensi di poterlo fare. Pertanto, in questo caso puoi evitare di usare Constructor , altrimenti avresti throw some exception se un'istanza esiste già con il nome specificato.

Quindi, puoi creare un metodo factory statico: - getInstanceWithName(name) che otterrà l'istanza già disponibile con quel nome e, se non esiste, creerà una nuova istanza e renderà constructor private, come dovrebbe essere fatto principalmente quando si tratta di static factories .

Inoltre, per questo devi mantenere un List o Map statico di tutte le istanze univoche create, nella tua classe Factory .

EDIT : -

Dovresti certamente passare attraverso - Java efficace - Articolo 1: Considera le fabbriche statiche sui costruttori . Non puoi ottenere una spiegazione migliore di quel libro.

    
risposta data 22.10.2012 - 19:51
fonte
5

Le menzioni di Efficace Java sembrano aggiungere molta credibilità, quindi questa risposta si basa su:

  • Elemento Java efficace 8: Rispettare il contratto generale quando si esegue l'override uguale a
  • Elemento Java efficace 9: Sostituisci sempre hashCode quando esegui l'override su
  • Elemento Java efficace 15: minimizza la mutevolezza

Vorrei fare un passo indietro e chiedermi perché ti preoccupi se ci sono più di un'istanza di questo oggetto nome.

Raramente ho bisogno di fare questo tipo di pool di oggetti. È mia opinione che l'OP stia facendo questo in modo che possano semplicemente confrontare i loro oggetti Name con == . Oppure utilizza gli oggetti Name all'interno di HashMap o simile alla chiave.

Se è così, questo è qualcosa che può essere risolto tramite corretta implementazione di equals() .

Così:

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

Una volta fatto il seguente è vero:

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

Sto indovinando il tuo obiettivo, ma in questo modo puoi utilizzare la classe Name nelle mappe di hash, ecc. e non devono essere esattamente la stessa istanza.

    
risposta data 22.10.2012 - 20:08
fonte
3
  • Crea Name un'interfaccia
  • Crea un'interfaccia NameFactory con un metodo Name getByName(String)
  • Crea un'implementazione di NameFactory con Map<String,WeakReference<Name>> al suo interno
  • synchronize sulla mappa per nome all'interno del metodo getByName prima di creare nuove istanze di Name
  • Facoltativamente, utilizza un'implementazione privata statica dell'interfaccia Name all'interno dell'implementazione di NameFactory

Questo approccio ti consente di garantire che:

  • Esiste solo una singola istanza di Name in qualsiasi momento
  • Non c'è perdita di memoria di "Lingerer" nella tua classe, quando Name s rimane più a lungo del necessario,
  • Il design rimane testabile con oggetti derisi, perché si usano le interfacce.
risposta data 22.10.2012 - 20:12
fonte
1

dovresti rendere privati i costruttori e creare metodi come getNameInstance(String) , se esiste già un oggetto con lo stesso nome (basato su un hastable della classe statica, per esempio), restituisci tale riferimento, altrimenti crei un nuovo oggetto usando il tuo costruttore privato e aggiungilo alla tabella hash

    
risposta data 22.10.2012 - 19:58
fonte
1

Prova a seguire. Devi tenere traccia di ogni oggetto che crei. Per questo scopo sto usando List. E ha reso privato il costruttore di classi, in modo che il controllo preliminare possa essere applicato prima di creare un'istanza

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;
        }
    }
    
risposta data 23.10.2012 - 12:59
fonte

Leggi altre domande sui tag