Maneiras de garantir instâncias exclusivas de uma classe?

14

Estou procurando maneiras diferentes de garantir que cada instância de uma determinada classe seja uma instância identificável exclusivamente.

Por exemplo, eu tenho uma Nameclasse com o campo name. Depois de ter um Nameobjeto nameinicializado para John Smith, não quero instanciar um objeto diferente.Name objeto também com o nome John Smith, ou, se ocorrer instanciação, desejo que uma referência ao objeto original seja passada de volta. do que um novo objeto.

Estou ciente de que uma maneira de fazer isso é ter uma fábrica estática que contenha Maptodos os objetos Name atuais e a fábrica verifique se um objeto com John Smith como o nome ainda não existe antes de retornar uma referência a umName objeto.

Outra maneira que eu poderia pensar é ter um mapa estático na Nameclasse e quando o construtor é chamado lançando uma exceção se o valor passado namejá estiver em uso em outro objeto, no entanto, estou ciente lançar exceções em um construtor é geralmente uma má ideia .

Existem outras maneiras de conseguir isso?

Amendoim
fonte
1
Você quer um singleton
5
é melhor você ir com o primeiro: - fábrica estática
Rohit Jain
16
@MarcB op não quer singleton. ele pode ter muitas instâncias da mesma classe, mas essas instâncias devem ser identificáveis.
1
@ MarcB Estou ciente do padrão singleton, mas pensei que garante apenas uma instância de uma classe é possível? Eu quero várias instâncias, valores diferentes. Desculpe se a pergunta não está deixando isso claro. editar: só vi o primeiro comentário antes de postar.
Amendoim
2
I'm aware that one way of doing this is to have a static factory that holds a Map...Então, por que você não quer fazer dessa maneira?
FrustratedWithFormsDesigner

Respostas:

13

Na verdade você já respondeu sua pergunta. Seu primeiro caminho deve ser mais eficaz aqui. Usar static factoryé sempre preferível do que constructoronde você acha que pode. Portanto, você pode evitar o uso Constructorneste caso, caso contrário, você teriathrow some exception se uma instância já existir com o nome fornecido.

Portanto, você pode criar um método estático de fábrica: - getInstanceWithName(name)que obterá a instância já disponível com esse nome e, se não existir, criará uma nova instância e tornará sua constructorprivada, como geralmente deve ser feito ao lidar com static factories.

Além disso, para isso, é necessário manter uma estática Listou Maptodas as instâncias exclusivas criadas, em sua Factoryclasse.

EDIT : -

Você certamente deve passar por - Java Efetivo - Item # 1: Considere fábricas estáticas em vez de construtores . Você não pode obter uma explicação melhor do que esse livro.

Rohit Jain
fonte
1
Isso é algo em que o OP já pensou.
BGurung
+1 no link, que parece abordar claramente algumas das preocupações do OP.
Robert Harvey
@RobertHarvey. Sim, esse livro é a melhor fonte para a maioria dos tópicos como esses. E esse é o primeiro item de fato. :)
Rohit Jain
+1 para Java efetivo, eu tenho o livro, agora na minha bolsa :) Na verdade, eu estava curioso sobre outras maneiras de obter a singularidade além do método estático de fábrica / estático e da exceção no construtor. Se não houver, eu aceito.
Amendoim
@Amendoim. Certo. Esse é o melhor recurso para obter mais informações.
Rohit Jain
5

As menções ao Java efetivo parecem agregar muita credibilidade, portanto, esta resposta se baseia:

  • Item efetivo de Java 8: Obedeça ao contrato geral ao substituir igual
  • Item efetivo do Java 9: ​​sempre substitua o hashCode ao substituir igual
  • Item 15 efetivo do Java: minimizar a mutabilidade

Eu daria um passo para trás e perguntaria por que você se importa se houver mais de uma instância desse objeto de nome.

Eu raramente preciso fazer esse tipo de pool de objetos. Acho que o OP está fazendo isso para que eles possam simplesmente comparar seus Nameobjetos ==. Ou use os Nameobjetos dentro de um HashMapou similar como a chave.

Se assim for, isso é algo que pode ser resolvido através da implementação adequada deequals() .

Igual a:

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

Feito isso, o seguinte é verdadeiro:

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

Estou adivinhando o seu objetivo, mas dessa forma você pode usar a Nameclasse em mapas de hash, etc., e eles não precisam ser exatamente a mesma instância.

Weston
fonte
Exemplo, por favor.
Robert Harvey
@RobertHarvey exemplo de implementação adequada?
weston
Parece que você forneceu um. Mas não estou claro como isso é diferente de apenas verificar a propriedade Name quanto à igualdade em um método estático de fábrica.
Robert Harvey
1
@RobertHarvey Tentei explicar mais, estou oferecendo uma alternativa completa que não requer fábrica estática e estou desafiando o ponto de partida dos OPs que eles não desejam ter mais de uma instância por nome.
weston
Um motivo válido para exigir objetos exclusivos é se você deseja que um bloco sincronizado use o objeto como um monitor. Dois objetos que .equals () não funcionariam.
Leigh Caldwell
3
  • Faça Nameuma interface
  • Crie uma interface NameFactorycom um métodoName getByName(String)
  • Crie uma implementação de NameFactory com um Map<String,WeakReference<Name>>dentro dele
  • synchronize no mapa pelo nome dentro do getByName método antes de criar novas instâncias deName
  • Opcionalmente, use uma implementação privada estática da Nameinterface dentro de sua implementação doNameFactory

Essa abordagem permitiria garantir que:

  • Somente uma única instância Nameexiste a qualquer momento,
  • Não há vazamento de memória "Lingerer" em sua classe, quando ele Namefica mais tempo do que o necessário,
  • O design permanece testável com objetos simulados, porque você usa interfaces.
dasblinkenlight
fonte
Também não há vazamentos de memória com um método de fábrica, se throwexistir um nome idêntico em vez de retornar um objeto ou se você retornar um nullobjeto.
Robert Harvey
@RobertHarvey, quero dizer os vazamentos que não são realmente vazamentos, mas objetos legítimos (os chamados "atrasados"). Um mapa estático simples impediria que seus objetos de valor fossem liberados, enquanto um mapa de referências fracas os manteria na memória apenas enquanto existirem outras referências ativas.
dasblinkenlight
Ah, entendo o que você quer dizer. Esse pode ser um dos casos raros em que um destructorseria útil.
Robert Harvey
1
Muito complexo. Isso pode ser feito como a resposta @weston, substituir iguais e fazer com que a fábrica mantenha uma coleção de nomes.
Tulains Córdova 23/10/12
@ user1598390 Deve-se tornar o mais simples possível, mas não mais simples. A "solução" da weston não resolve o problema do OP (não há mapa) e uma coleção de nomes cria um vazamento de memória mais prolongado (sim, há vazamentos de memória em Java).
dasblinkenlight
1

você deve tornar os construtores privados e criar métodos como getNameInstance(String), se já existir um objeto com o mesmo nome (com base em uma classe estática 'hastable por exemplo), você retornar essa referência, caso contrário, criar um novo objeto usando seu construtor privado e adicioná-lo para a hashtable

HericDenis
fonte
Você repetiu o que eu disse no terceiro parágrafo da pergunta. Eu estava buscando maneiras alternativas.
Amendoim
Sinto muito, acho que respondemos quase ao mesmo tempo porque verifiquei as respostas antes de postar as minhas. Na verdade, tentei responder no StackOverflown e depois migrá-lo.
HericDenis 25/10/12
1

Tente seguir. Você precisa acompanhar cada objeto que criar. Para esse fim, estou usando a Lista. E tornou o construtor de classe privado, para que a pré-verificação possa ser aplicada antes de criar uma instância

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
fonte
Isto não é Java? Você escreveu um pseudo-código? Ou, em qualquer outro idioma? Por favor, especifique isso explicitamente.
Rohit Jain
Parece C #. Akshay, você deve aprender outras coleções que não List. Isso seria um bom ajuste para Dictionary<string,UniqueName>.
weston