¿Formas de garantizar instancias únicas de una clase?

14

Estoy buscando diferentes formas de garantizar que cada instancia de una clase determinada sea una instancia identificable de forma única.

Por ejemplo, tengo una Nameclase con el campo name. Una vez que tengo un Nameobjeto nameinicializado a John Smith, no quiero poder crear una instancia de un Nameobjeto diferente también con el nombre de John Smith, o si se produce una instanciación, quiero que se devuelva una referencia al objeto original. que un nuevo objeto

Soy consciente de que una forma de hacerlo es tener una fábrica estática que contenga Maptodos los objetos de Nombre actuales y la fábrica verifica que un objeto con John Smith como nombre ya no exista antes de devolver una referencia a un Nameobjeto.

Otra forma de pensar en la parte superior de mi cabeza es tener un Mapa estático en la Nameclase y cuando se llama al constructor lanzando una excepción si el valor pasado nameya está en uso en otro objeto, sin embargo, soy consciente de lanzar excepciones en un constructor es generalmente una mala idea .

¿Hay otras formas de lograr esto?

Maní
fuente
1
Quieres un singleton
55
deberías ir con el primero: - fábrica estática
Rohit Jain
16
@MarcB op no quiere singleton. él podría tener muchas instancias de la misma clase, pero estas instancias deben ser identificables.
1
@MarcB ¿Conozco el patrón singleton pero pensé que eso garantiza que solo sea posible una instancia de una clase? Quiero múltiples instancias, diferentes valores. Lo siento si la pregunta no lo deja claro. editar: solo vi el primer comentario antes de publicar.
Maní
2
I'm aware that one way of doing this is to have a static factory that holds a Map...Entonces, ¿por qué no quieres hacerlo de esta manera?
FrustratedWithFormsDesigner

Respuestas:

13

En realidad ya has respondido tu pregunta. Su primera forma debería ser más efectiva aquí. Usar static factoryes siempre preferible que constructordonde creas que puedes. Por lo tanto, puede evitar el uso Constructoren este caso, de lo contrario lo haría throw some exceptionsi ya existiera una instancia con el nombre dado.

Por lo tanto, puede crear un método de fábrica estático: getInstanceWithName(name)que obtendrá la instancia ya disponible con ese nombre, y si no existe, creará una nueva instancia y la hará constructorprivada, como debería hacerse principalmente cuando se trata de static factories.

Además, para eso necesita mantener una estática Listo Mapde todas las instancias únicas creadas, en su Factoryclase.

EDITAR : -

Sin duda, debe pasar por: Java efectivo: elemento n. ° 1: considere las fábricas estáticas sobre los constructores . No se puede obtener una mejor explicación que ese libro.

Rohit Jain
fuente
1
Eso es algo en lo que el OP ya ha pensado.
BGurung
+1 para el enlace, que parece abordar claramente algunas de las preocupaciones del OP.
Robert Harvey
@RobertHarvey. Sí, ese libro es la mejor fuente para la mayoría de los temas como estos. Y ese es el primer elemento de hecho. :)
Rohit Jain
+1 para Java efectivo, tengo el libro, ahora en mi bolso :) Sin embargo, tenía curiosidad sobre otras formas de lograr la unicidad aparte del método estático de fábrica / estático y la excepción en el constructor. Si no hay ninguno, entonces aceptaré esto.
Maní
@Maní. Seguro. Ese es el mejor recurso para cavar para obtener más información.
Rohit Jain
5

Las menciones de Java efectivo parecen agregar mucha credibilidad, por lo que esta respuesta se basa en:

  • Artículo 8 de Java efectivo: Obedecer el contrato general cuando se anula igual
  • Elemento de Java efectivo 9: anular siempre el hashCode cuando anule iguales
  • Artículo eficaz de Java 15: minimizar la mutabilidad

Daría un paso atrás y le preguntaría por qué le importa si hay más de una instancia de este objeto de nombre.

Raramente necesito hacer este tipo de agrupación de objetos. Supongo que el OP está haciendo esto para que simplemente puedan comparar sus Nameobjetos ==. O use los Nameobjetos dentro de a HashMapo similar como la clave.

Si es así, esto es algo que se puede resolver mediante la implementación adecuada deequals() .

Al igual que:

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 vez hecho esto, lo siguiente es cierto:

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

Supongo que su objetivo, pero de esta manera puede usar la Nameclase en mapas hash, etc., y no tienen que ser exactamente la misma instancia.

Weston
fuente
Ejemplo, por favor.
Robert Harvey
@RobertHarvey ejemplo de implementación adecuada?
Weston
Parece que proporcionaste uno. Pero no estoy claro cómo esto es diferente de simplemente verificar la igualdad de la propiedad Name en un método de fábrica estático.
Robert Harvey
1
@RobertHarvey He tratado de explicar más, estoy ofreciendo una alternativa completa que no requiere una fábrica estática y estoy desafiando el punto de partida de los OP que no quieren tener más de una instancia por nombre.
Weston
Una razón válida para requerir objetos únicos es si desea que un bloque sincronizado use el objeto como monitor. Dos objetos que .equals () entre sí no funcionarían.
Leigh Caldwell
3
  • Hacer Nameuna interfaz
  • Crea una interfaz NameFactorycon un métodoName getByName(String)
  • Crea una implementación NameFactorycon un Map<String,WeakReference<Name>>interior
  • synchronizeen el mapa por nombre dentro del getByNamemétodo antes de hacer nuevas instancias deName
  • Opcionalmente, use una implementación privada estática de la Nameinterfaz dentro de su implementación delNameFactory

Este enfoque le permitiría asegurarse de que:

  • Solo Nameexiste una única instancia de en cualquier momento,
  • No hay pérdida de memoria "Lingerer" en su clase, cuando se Namequedan más tiempo del necesario,
  • El diseño sigue siendo comprobable con los objetos simulados, ya que utiliza interfaces.
dasblinkenlight
fuente
Tampoco tiene pérdidas de memoria con un método de fábrica, si throwexiste un nombre idéntico en lugar de devolver un objeto, o si devuelve un nullobjeto.
Robert Harvey
@RobertHarvey Me refiero a las fugas que no son realmente fugas, sino objetos legítimos (los llamados "lingerers"). Un mapa estático simple evitaría que se liberen sus objetos de valor, mientras que un mapa de referencias débiles los mantendría en la memoria solo mientras existan otras referencias en vivo.
dasblinkenlight
Ah, ya veo lo que quieres decir. Este podría ser uno de los raros casos en que a destructorsería útil.
Robert Harvey
1
Demasiado complejo Se puede hacer como @weston answer, anular iguales y hacer que la fábrica mantenga una colección de nombres.
Tulains Córdova
@ user1598390 Uno debería hacer las cosas lo más simple posible, pero no más simple. La "solución" de Weston no resuelve el problema del OP (no hay un mapa), y una colección de nombres crea una pérdida de memoria persistente (sí, hay pérdidas de memoria en Java).
dasblinkenlight
1

debe hacer que los constructores sean privados y crear métodos como getNameInstance(String), si un objeto con el mismo nombre ya existe (basado en una clase estática 'hastable por ejemplo), devuelve esa referencia, de lo contrario, crea un nuevo objeto usando su constructor privado y lo agrega a la tabla hash

HericDenis
fuente
Has repetido lo que dije en el tercer párrafo de la pregunta. Estaba buscando formas alternativas.
Maní
Lo siento, creo que respondimos casi al mismo tiempo porque busqué las respuestas antes de publicar las mías. En realidad, traté de responderlo en StackOverflown y luego migraron.
HericDenis
1

Intenta seguir. Debe realizar un seguimiento de cada objeto que cree. Para este propósito estoy usando List. E hizo que el constructor de clases fuera privado, de modo que la verificación previa se pueda aplicar antes de crear una instancia

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
fuente
¿Esto no es Java? ¿Has escrito un pseudocódigo? ¿O en algún otro idioma? Por favor especifique eso explícitamente.
Rohit Jain
Parece que C #. Akshay, deberías aprender otras colecciones que no sean List. Esto sería una buena opción para Dictionary<string,UniqueName>.
Weston