web-dev-qa-db-fra.com

Comment garantir des instances uniques d'une classe?

Je recherche différentes façons de garantir que chaque instance d'une classe donnée est une instance identifiable de manière unique.

Par exemple, j'ai une classe Name avec le champ name. Une fois que j'ai un objet Name avec name initialisé à John Smith, je ne veux plus pouvoir instancier un autre objet Name avec le nom de John Smith, ou si l'instanciation a lieu, je veux qu'une référence à l'objet d'origine soit renvoyée plutôt qu'un nouvel objet.

Je suis conscient qu'une façon de faire est d'avoir une fabrique statique qui contient un Map de tous les objets Name actuels et la fabrique vérifie qu'un objet avec John Smith comme nom n'existe pas déjà renvoyer une référence à un objet Name.

Une autre façon dont je pourrais penser du haut de ma tête est d'avoir une carte statique dans la classe Name et lorsque le constructeur est appelé levant une exception si la valeur transmise pour name est déjà dans utiliser dans un autre objet, cependant je suis conscient lancer des exceptions dans un constructeur est généralement une mauvaise idée .

Y a-t-il d'autres moyens d'y parvenir?

14
Peanut

En fait, vous avez déjà répondu à votre question. Votre première façon devrait être plus efficace ici. Utiliser static factory Est toujours préférable à constructor partout où vous pensez que vous le pouvez. Ainsi, vous pouvez éviter d'utiliser Constructor dans ce cas, sinon vous auriez throw some exception Si une instance existe déjà avec le nom donné.

Ainsi, vous pouvez créer une méthode d'usine statique: - getInstanceWithName(name) qui obtiendra l'instance déjà disponible avec ce nom, et si elle n'existe pas, elle créera une nouvelle instance et fera votre constructor privé, comme cela doit être fait principalement lorsque vous traitez avec static factories.

De plus, pour cela, vous devez conserver un List ou Map statique de toutes les instances uniques créées, dans votre classe Factory.

[~ # ~] modifier [~ # ~] : -

Vous devriez certainement passer par - Efficace Java - Item # 1: Considérez les usines statiques plutôt que les constructeurs . Vous ne pouvez pas obtenir une meilleure explication que ce livre.

13
Rohit Jain

Mentions de Java efficace semble ajouter beaucoup de crédibilité, donc cette réponse s'appuie sur:

  • Efficace Java Item 8: Obéissez au contrat général lorsque vous remplacez égal à
  • Efficace Java Item 9: Toujours remplacer le hashCode lorsque vous remplacez equals
  • Efficace Java Item 15: Minimiser la mutabilité

Je prendrais un peu de recul et me demander pourquoi vous vous souciez s'il y a plus d'une instance de cet objet de nom.

J'ai rarement besoin de faire ce type de mise en commun d'objets. Je pense que l'OP fait cela afin qu'ils puissent simplement comparer leurs objets Name avec ==. Ou utilisez les objets Name à l'intérieur d'un HashMap ou similaire comme clé.

Si c'est le cas, c'est quelque chose qui peut être résolu par implémentation correcte de equals() .

Ainsi:

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

Une fois cela fait, ce qui suit est vrai:

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

Je devine votre objectif, mais de cette façon, vous pouvez utiliser la classe Name dans les cartes de hachage, etc., et elles pas doivent être exactement la même instance.

5
weston
  • Faire de Name une interface
  • Créer une interface NameFactory avec une méthode Name getByName(String)
  • Créez une implémentation de NameFactory avec un Map<String,WeakReference<Name>> à l'intérieur
  • synchronize sur la carte par nom à l'intérieur de la méthode getByName avant de créer de nouvelles instances de Name
  • Facultativement, utilisez une implémentation privée statique de l'interface Name dans votre implémentation de NameFactory

Cette approche vous permettrait de vous assurer que:

  • Une seule instance de Name existe à tout moment,
  • Il n'y a pas de fuite de mémoire "Lingerer" dans votre classe, lorsque les Names restent plus longtemps que nécessaire,
  • La conception reste testable avec des objets simulés, car vous utilisez des interfaces.
3
dasblinkenlight

vous devez rendre les constructeurs privés et créer des méthodes comme getNameInstance(String), si un objet du même nom existe déjà (basé sur une classe statique hastable par exemple), vous retournez cette référence, sinon vous créez un nouvel objet en utilisant votre constructeur privé et ajoutez-le à la table de hachage

1
HericDenis

Essayez de suivre. Vous devez garder une trace de chaque objet que vous créez. À cette fin, j'utilise List. Et a rendu le constructeur de classe privé, afin que la pré-vérification puisse être appliquée avant de créer une instance

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;
        }
    }
1
Akshay