Formas 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 exclusivamente identificável.

Por exemplo, tenho uma classe Name com o campo name . Quando tiver um objeto Name com name inicializado como John Smith, não quero poder instanciar um objeto Name diferente também com o nome como John Smith ou, se ocorrer uma instanciação, quero uma referência para o objeto original a ser passado de volta ao invés de um novo objeto.

Estou ciente de que uma maneira de fazer isso é ter um factory estático que contém Map de todos os objetos Name atuais e a fábrica verifica se um objeto com John Smith como nome já não existe antes retornando uma referência a um objeto Name .

Outra maneira de pensar em cima é ter um Mapa estático na classe Name e quando o construtor é chamado lançando uma exceção se o valor passado para name já 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?

    
por Peanut 22.10.2012 / 19:44
fonte

5 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 constructor onde quer que você ache que pode. Assim, você pode evitar usar Constructor neste caso, senão você teria throw some exception se já existir uma instância com o nome fornecido.

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

Além disso, para isso, você precisa manter um static List ou Map de todas as instâncias exclusivas criadas, na sua classe Factory .

EDITAR : -

Você certamente deve seguir - Java efetivo - Item # 1: Considere fábricas estáticas sobre construtores . Você não pode obter uma explicação melhor do que esse livro.

    
por 22.10.2012 / 19:51
fonte
5

As menções de Java efetivo parecem adicionar muita credibilidade, então essa resposta é baseada em:

  • Item 9 de Java efetivo: Obedeça ao contrato geral ao substituir equals
  • Java efetivo Item 9: Sempre substitua o hashCode quando você substitui é igual a
  • Item de Java efetivo 15: 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. É meu palpite que o OP está fazendo isso para que eles possam simplesmente comparar seus objetos Name com == . Ou use os objetos Name dentro de um HashMap ou semelhante à chave.

Nesse caso, isso é algo que pode ser resolvido por meio da implementação adequada de equals() .

Assim:

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

Depois de concluído, 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

Eu estou supondo que seu objetivo, mas desta forma você pode usar a classe Name em mapas de hash, etc, e não tem que ser exatamente a mesma instância.

    
por 22.10.2012 / 20:08
fonte
3
  • Crie Name uma interface
  • Crie uma interface NameFactory com um método Name getByName(String)
  • Crie uma implementação de NameFactory com um Map<String,WeakReference<Name>> dentro dela
  • synchronize no mapa pelo nome dentro do método getByName antes de criar novas instâncias de Name
  • Opcionalmente, use uma implementação privada estática da interface Name dentro de sua implementação do NameFactory

Essa abordagem permite que você garanta que:

  • Apenas uma única instância de Name existe a qualquer momento,
  • Não há vazamento de memória "Lingerer" na sua turma, quando Name s permanecer por mais tempo do que o necessário,
  • O design permanece testável com objetos ridicularizados, porque você usa interfaces.
por 22.10.2012 / 20:12
fonte
1

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

    
por 22.10.2012 / 19:58
fonte
1

Tente seguir. Você precisa acompanhar cada objeto criado. Para este propósito, 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;
        }
    }
    
por 23.10.2012 / 12:59
fonte