Eu estava lendo o artigo de Singleton na Wikipedia e me deparei com este exemplo:
public class Singleton {
// Private constructor prevents instantiation from other classes
private Singleton() {}
/**
* SingletonHolder is loaded on the first execution of Singleton.getInstance()
* or the first access to SingletonHolder.INSTANCE, not before.
*/
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
Embora eu realmente goste da maneira como esse Singleton se comporta, não consigo ver como adaptá-lo para incorporar argumentos ao construtor. Qual é a maneira preferida de fazer isso em Java? Eu teria que fazer algo assim?
public class Singleton
{
private static Singleton singleton = null;
private final int x;
private Singleton(int x) {
this.x = x;
}
public synchronized static Singleton getInstance(int x) {
if(singleton == null) singleton = new Singleton(x);
return singleton;
}
}
Obrigado!
Edit: Eu acho que comecei uma tempestade de controvérsia com o meu desejo de usar Singleton. Deixe-me explicar minha motivação e espero que alguém possa sugerir uma idéia melhor. Estou usando uma estrutura de computação em grade para executar tarefas em paralelo. Em geral, eu tenho algo parecido com isto:
// AbstractTask implements Serializable
public class Task extends AbstractTask
{
private final ReferenceToReallyBigObject object;
public Task(ReferenceToReallyBigObject object)
{
this.object = object;
}
public void run()
{
// Do some stuff with the object (which is immutable).
}
}
O que acontece é que, embora eu apenas passe uma referência aos meus dados para todas as tarefas, quando as tarefas são serializadas, os dados são copiados repetidamente. O que eu quero fazer é compartilhar o objeto entre todas as tarefas. Naturalmente, eu posso modificar a classe da seguinte maneira:
// AbstractTask implements Serializable
public class Task extends AbstractTask
{
private static ReferenceToReallyBigObject object = null;
private final String filePath;
public Task(String filePath)
{
this.filePath = filePath;
}
public void run()
{
synchronized(this)
{
if(object == null)
{
ObjectReader reader = new ObjectReader(filePath);
object = reader.read();
}
}
// Do some stuff with the object (which is immutable).
}
}
Como você pode ver, mesmo aqui, tenho o problema de que a passagem de um caminho de arquivo diferente não significa nada após a passagem do primeiro. É por isso que gosto da ideia de uma loja que foi postada nas respostas. De qualquer forma, em vez de incluir a lógica para carregar o arquivo no método run, eu queria abstrair essa lógica em uma classe Singleton. Não darei mais outro exemplo, mas espero que você entenda. Deixe-me ouvir suas idéias para uma maneira mais elegante de realizar o que estou tentando fazer. Mais uma vez obrigado!
Respostas:
Vou deixar meu argumento bem claro: um singleton com parâmetros não é um singleton .
Um singleton, por definição, é um objeto que você deseja instanciar não mais que uma vez. Se você está tentando alimentar parâmetros para o construtor, qual é o objetivo do singleton?
Você tem duas opções. Se você deseja que seu singleton seja inicializado com alguns dados, você pode carregá-lo com dados após a instanciação , da seguinte maneira:
Se a operação que seu singleton está executando for recorrente e com parâmetros diferentes a cada vez, você também pode passar os parâmetros para o método principal que está sendo executado:
Em qualquer caso, a instanciação será sempre sem parâmetros. Caso contrário, seu singleton não é um singleton.
fonte
Eu acho que você precisa de algo como um fábrica para ter objetos com vários parâmetros instanciados e reutilizados. Ele pode ser implementado usando um parâmetro sincronizado
HashMap
ouConcurrentHashMap
mapeado (Integer
por exemplo) para sua classe parametrizável 'singleton'.Embora você possa chegar ao ponto em que deve usar classes regulares e não singleton (por exemplo, precisando de 10.000 singleton com parâmetros diferentes).
Aqui está um exemplo para essa loja:
Para avançar ainda mais, os Java
enum
s também podem ser considerados (ou usados como) singletons parametrizados, embora permitindo apenas um número fixo de variantes estáticas.No entanto, se você precisar de uma solução distribuída 1 , considere alguma solução de armazenamento em cache lateral. Por exemplo: EHCache, Terracota, etc.
1 no sentido de estender várias VMs em provavelmente vários computadores.
fonte
Você pode adicionar um método de inicialização configurável para separar a instanciação da obtenção.
Em seguida, você pode ligar
Singleton.init(123)
uma vez para configurá-lo, por exemplo, na inicialização do aplicativo.fonte
Você também pode usar o padrão Builder se desejar mostrar que alguns parâmetros são obrigatórios.
Então você pode criar / instanciar / parametrizar da seguinte maneira:
fonte
A instrução " um singleton com parâmetros não é um singleton " não está completamente correta . Precisamos analisar isso da perspectiva do aplicativo e não da perspectiva do código.
Construímos a classe singleton para criar uma única instância de um objeto em uma execução de aplicativo. Ao ter um construtor com o parâmetro, você pode criar flexibilidade no seu código para alterar alguns atributos do seu objeto singleton toda vez que você executa o aplicativo. Isso não é uma violação do padrão Singleton. Parece uma violação se você ver isso da perspectiva do código.
Os Padrões de Design estão lá para nos ajudar a escrever código flexível e extensível, para não nos impedir de escrever um bom código.
fonte
Use getters e setters para definir a variável e tornar o construtor padrão privado. Então use:
fonte
Surpreso que ninguém tenha mencionado como um criador de logs é criado / recuperado. Por exemplo, abaixo mostra como o log4J logger é recuperado.
Existem alguns níveis de indireções, mas a parte principal é o método abaixo , que praticamente conta tudo sobre como funciona. Ele usa uma tabela de hash para armazenar os registradores existentes e a chave é derivada do nome. Se o criador de logs não existir para um nome, ele usa uma fábrica para criar o criador de logs e o adiciona à tabela de hash.
fonte
Modificação do padrão Singleton que usa o idioma de titular da demanda de inicialização de Bill Pugh . Isso é seguro para threads sem a sobrecarga de construções de idiomas especializadas (ou seja, voláteis ou sincronizadas):
fonte
finally { RInterfaceHL.rloopHandler = null; }
emgetInstance
, porque essa referência estática pode causar um vazamento de memória, se não formos cuidadosos. No seu caso, parece que não é um problema, mas eu poderia imaginar um cenário em que o objeto passado é grande e usado apenas peloRInterfaceHL
ctor para obter alguns valores e não para manter uma referência a ele.return SingletonHolder.INSTANCE
funcionaria tão bem quantogetInstance
. Não acho que haja uma necessidade de encapsulamento aqui, porque a classe externa já conhece as entranhas da classe interna, elas estão fortemente acopladas: sabe querloopHandler
precisa de init antes de chamar. Além disso, o construtor privado não tem efeito, porque o material privado da classe interna está simplesmente disponível para a classe externa.A razão pela qual você não consegue entender como realizar o que está tentando fazer é provavelmente que o que está tentando fazer realmente não faz sentido. Você quer chamar
getInstance(x)
com argumentos diferentes, mas sempre retorna o mesmo objeto? Qual é o comportamento que você deseja quando ligagetInstance(2)
e depoisgetInstance(5)
?Se você deseja que o mesmo objeto, mas que seu valor interno seja diferente, que é a única maneira de ainda ser um singleton, não precisará se preocupar com o construtor; você acabou de definir o valor na
getInstance()
saída do objeto. Obviamente, você entende que todas as suas outras referências ao singleton agora têm um valor interno diferente.Se você deseja
getInstance(2)
egetInstance(5)
retorna objetos diferentes, por outro lado, não está usando o padrão Singleton, está usando o padrão Factory.fonte
No seu exemplo, você não está usando um singleton. Observe que se você fizer o seguinte (assumindo que o Singleton.getInstance seja realmente estático):
Então os valores do obj2.x são 3, não 4. Se você precisar fazer isso, torne-a uma classe simples. Se o número de valores for pequeno e fixo, você pode considerar o uso de um
enum
. Se você estiver tendo problemas com a geração excessiva de objetos (o que geralmente não é o caso), considere os valores de armazenamento em cache (e verifique as fontes ou obtenha ajuda com isso, pois é óbvio como criar caches sem o perigo de vazamento de memória).Você também pode ler este artigo, pois os singletons podem ser usados com muita facilidade.
fonte
Outra razão pela qual os Singletons são um antipadrão é que, se escritos de acordo com as recomendações, com construtor privado, são muito difíceis de subclassificar e configurar para usar em determinados testes de unidade. Seria necessário manter o código legado, por exemplo.
fonte
Se você deseja criar uma classe Singleton servindo como um contexto, uma boa maneira é ter um arquivo de configuração e ler os parâmetros do arquivo dentro da instância ().
Se os parâmetros que alimentam a classe Singleton forem obtidos dinamicamente durante a execução do seu programa, basta usar um HashMap estático armazenando instâncias diferentes na sua classe Singleton para garantir que, para cada parâmetro, apenas uma instância seja criada.
fonte
Isso não é um singleton, mas pode ser algo que pode resolver seu problema.
fonte
Se considerarmos o problema como "como fazer singleton com estado", não será necessário passar o estado como parâmetro construtor. Concordo com as postagens que inicializam os estados ou usando o método set depois de obter a instância singleton.
Outra pergunta é: é bom ter singleton com estado?
fonte
Não poderíamos fazer algo assim:
fonte
Apesar do que alguns possam afirmar, aqui está um singleton com parâmetros no construtor
O padrão singleton diz:
que são respeitados com este exemplo.
Por que não definir diretamente a propriedade? É o caso do livro didático para mostrar como podemos obter um singleton com construtor com parâmetro, mas pode ser útil em algumas situações. Por exemplo, em casos de herança para forçar o singleton a definir algumas propriedades da superclasse.
fonte
Estou com medo de postar isso como resposta, mas não entendo por que ninguém pensa sobre isso, talvez essa resposta também já tenha sido dada e eu simplesmente não entendi.
Como os
getInstance()
retornos sempre são a mesma instância, acho que isso poderia funcionar. Se isso estiver errado demais, vou excluí-lo, apenas estou interessado neste tópico.fonte
Eu acho que isso é um problema comum. Separar a "inicialização" do singleton do "get" do singleton pode funcionar (este exemplo usa uma variação do bloqueio verificado duas vezes).
fonte
Singleton é, obviamente, um "antipadrão" (assumindo uma definição de estática com estado variável).
Se você deseja um conjunto fixo de objetos de valor imutável, as enumerações são o caminho a seguir. Para um conjunto grande e possivelmente aberto de valores, você pode usar um Repositório de alguma forma - geralmente com base em uma
Map
implementação. Obviamente, quando você estiver lidando com estática, tenha cuidado com o encadeamento (sincronize suficientemente o suficiente ou use umaConcurrentMap
verificação para verificar se outro encadeamento não o venceu ou use algum tipo de futuro).fonte
Singletons são geralmente considerados anti-padrões e não devem ser usados. Eles não facilitam o teste de código.
Um singleton com um argumento não faz sentido de qualquer maneira - o que aconteceria se você escrevesse:
Seu singleton também não é seguro para threads, pois vários threads podem fazer chamadas simultâneas,
getInstance
resultando na criação de mais de uma instância (possivelmente com valores diferentes dex
).fonte