Singleton - qual a melhor implementação?

45 respostas
E

O livro de Padrões de Projeto - Use a Cabeça - nos ensina a usar pelo menos duas implementações de singleton.

public class Singleton
{
	private static Singleton singleton;

	private Singleton(){}

	public synchronized Singleton getInstance()
	{
		if(singleton == null)
			singleton = new Singleton();

		return singleton;
	}
}

Essa primeira implementação utiliza um método sincronizado. O livro diz que sincronizar um método é caro (Isso é verdade, rsrs). Se várias threads estiverem sempre querendo adquirir uma referência ao objeto singleton, pode haver uma queda de desempenho, já que antes de entrar no método sincronizado, tem-se que obter o bloqueio.

A segunda implementação utiliza uma variável estática.

public class Singleton
{
	private static Singleton singleton = new Singleton();

	private Singleton(){}

	public Singleton getInstance()
	{
		return singleton;
	}
}

O livro não fala muita coisa a respeito desse segundo método. Ao meu ver, remove a obrigatoriedade de ter de obter o bloqueio do método. Quando a classe é carregada, um objeto singleton já é criado. Qualquer thread pode obter uma referência a esse objeto. Sempre irão obter uma referência ao mesmo objeto e entrar mais de uma thread no método getInstance não causa problema algum.

Assim, porque usaríamos o primeiro código em vez do segundo? Alguém poderia me fazer o favor de explicar mais pontos positivos e negativos do primeiro e segundo código?

45 Respostas

D

Se é para usar uma variável estática e você está em um projeto com a versão do Java >= 1.5, considere o uso de Enums.
Enums, por excelência, são Singletons e, tal qual variáveis estáticas, são carregados na inicialização do projeto.
Aqui um exemplo.

D

Edit: Duplicou ao enviar.

D

Por mais básica que seja essa resposta, acredito que a questão depende da sua necessidade de sincronizar ou não.

Para certos cenários, por mais caro que seja, ou você sincroniza ou você terá tantos problemas que performance será o que menos vc irá se preocupar.

L

A primeira forma não funciona como deve e não deve ser usada.

O recomenado é iniciar a váriavel na declaração mesmo.

private static Singleton singleton = new Singleton();

A primeira forma ainda tá faltando a sincronização dentro do método, visto que um objeto poderá ser iniciado várias vezes em chamadas simultaneas. Mesmo asssim não deve ser usado.

Leia mais buscando por ‘double checked locking in singleton’.

Explicado por que não usar:
http://www.javaworld.com/jw-02-2001/jw-0209-double.html?page=1

Detalhes:
http://www.ibm.com/developerworks/java/library/j-dcl/index.html
http://en.wikipedia.org/wiki/Double-checked_locking

E

Obrigado pelos posts…vou ler os links de double locks…

Mas ainda não sei qual dos dois métodos é melhor e porque o primeiro não funciona…

Ao meu ver, o segundo é mais rápido que o primeiro. Não sei porque alguém usaria o primeiro, já que tem o atraso da sincronização.
Com relação ao que disseram sobre o método getInstance() não estar sincronizado no segundo, não vejo necessidade. Ele apenas retorna uma referência.

S

ECO2004:

O livro não fala muita coisa a respeito desse segundo método. Ao meu ver, remove a obrigatoriedade de ter de obter o bloqueio do método. Quando a classe é carregada, um objeto singleton já é criado. Qualquer thread pode obter uma referência a esse objeto. Sempre irão obter uma referência ao mesmo objeto e entrar mais de uma thread no método getInstance não causa problema algum.

Assim, porque usaríamos o primeiro código em vez do segundo? Alguém poderia me fazer o favor de explicar mais pontos positivos e negativos do primeiro e segundo código?

O primeiro código é para que Lazy singleton. O lazy singleton é importante quanto o singleton é raramente usado e/ou é muito custoso em termos de recursos, e/ou não é garantido que existe. Desktop e Runtime são dois exemplos de singleton em java que raramente são usados e que quando são, custam. No caso do Desktop ele pode nem existir , e vai dar erro se tentar obter ele.

A melhor maneira de criar um singleton é não criar. A maior parte das vezes o que queremos é um Shared Object e não um singleton.
Singleton de verdade são raros em aplicações.

O lazy singleton é a regra para singletons de verdade porque normalmente é custoso criar o objeto ( por isso que ele é singleton para começo de conversa).
Para singletons de mentirinhas a instancia estática é melhor.

A implementação do padrão singleton que apresentou usa um Static Factory Method , mas o Effective Java demonstra que o singleton feito dessa forma é um objeto com muitos problemas técnicos (os singletons de verdade do java são controlados pela JVM, na realidade são “prespectivas” da JVM … ). Sobretudo se entrar serialização no barulho. A melhor forma atualmente para escreve um singleton é usar enum. Isso garante não apenas que ha uma instancia,como elimina um monte de problemas relativos a serilização e classloading.

Contudo, como disse, a melhor forma de implementar um singleton é não implementar.

O singleton é um padrão acadêmico que é usado em aula para estabelecer conceitos como static e “numero de instancias”, mas no mundo real ele não é muito prático e na realidade é muito complexo de implementar corretamente

V

A diferença está no fato que na primeira solução o singleton é lazy e na segunda não. Aí entra o desperdício, imagine que esse singleton é um objeto muito pesado, com a segunda solução ele seria carregado mesmo que você não o utilize, o que não acontece na primeira solução.

Falando sobre concorrência, é justamente essa a dificuldade de chegar a uma solução, conseguir fazer com que o singleton seja lazy. A sua primeira solução é assim, mas tem um grande problema: mesmo depois da inicialização, o acesso à instância ainda continua sendo synchronized, o que pode afetar muito a performance.

Eu ia omitir a resposta correta (não queria atrapalhar o seu aprendizado) mas ela já foi dada acima, double checked locking é a forma adequada de se fazer. A idéia é garantir que seja thread safe e ao mesmo tempo não mate a performance da aplicação.

Mas saiba que ainda há um problema relacionado à visibilidade por parte das threads, fica pra você estudar! :smiley:

E

Muito obrigado, pessoal!

Entendi o que disseram!

Abraço…

Wilson

S

von.juliano:
A diferença está no fato que na primeira solução o singleton é lazy e na segunda não. Aí entra o desperdício, imagine que esse singleton é um objeto muito pesado, com a segunda solução ele seria carregado mesmo que você não o utilize, o que não acontece na primeira solução.

Falando sobre concorrência, é justamente essa a dificuldade de chegar a uma solução, conseguir fazer com que o singleton seja lazy. A sua primeira solução é assim, mas tem um grande problema: mesmo depois da inicialização, o acesso à instância ainda continua sendo synchronized, o que pode afetar muito a performance.

Eu ia omitir a resposta correta (não queria atrapalhar o seu aprendizado) mas ela já foi dada acima, double checked locking é a forma adequada de se fazer.

double checked locking não é a forma correta de fazer. Isso é um anti-pattern. É só procurar no google.
Se vai usar loking a única forma correta é fazer o método inteiro ser sincronizado. É mais lento, sim. Mas é real. Não vai dar problemas. O double checking locking parece melhor, porque é mais rápido, mas tem muitos problemas não triviais que podem acontecer e ferrar com toda a lógica do sistema. Mais sobre isto aqui

O esquema é o seguinte:

  1. não use singleton. Não use mesmo. Muito provavelmente precisa de um Shared Object, não se um singleton. Singleton são raros!
  2. se vai usar singleton, então use enum para o implementar. Simples assim. Não tem o problema da sincronização nem muitos outros que a oslução classica que usa Static Factory Method tem.

Simples assim. Sem treta.
Para saber mais por favor leia o livro “Efective java” segunda edição. Aliás , quem não leu, leia. É bom para a cultura. :slight_smile:

V

@sergiotaborda A forma que ele mostrou está incompleta, mas o double checked locking seria a resposta correta se empregar o volatile. Ou estou enganado?

E

Certas coisas aparentemente precisam de um singleton, mas na verdade acabam precisando de um ThreadLocal (ou seja, uma instância do objeto por thread).

Isso ocorre quando a classe tem métodos que não são thread-safe. Um exemplo: classes SimpleDateFormat e DecimalFormat.

X

sergiotaborda:
ECO2004:

O livro não fala muita coisa a respeito desse segundo método. Ao meu ver, remove a obrigatoriedade de ter de obter o bloqueio do método. Quando a classe é carregada, um objeto singleton já é criado. Qualquer thread pode obter uma referência a esse objeto. Sempre irão obter uma referência ao mesmo objeto e entrar mais de uma thread no método getInstance não causa problema algum.

Assim, porque usaríamos o primeiro código em vez do segundo? Alguém poderia me fazer o favor de explicar mais pontos positivos e negativos do primeiro e segundo código?

O primeiro código é para que Lazy singleton. O lazy singleton é importante quanto o singleton é raramente usado e/ou é muito custoso em termos de recursos, e/ou não é garantido que existe. Desktop e Runtime são dois exemplos de singleton em java que raramente são usados e que quando são, custam. No caso do Desktop ele pode nem existir , e vai dar erro se tentar obter ele.

A melhor maneira de criar um singleton é não criar. A maior parte das vezes o que queremos é um Shared Object e não um singleton.
Singleton de verdade são raros em aplicações.

O lazy singleton é a regra para singletons de verdade porque normalmente é custoso criar o objeto ( por isso que ele é singleton para começo de conversa).
Para singletons de mentirinhas a instancia estática é melhor.

A implementação do padrão singleton que apresentou usa um Static Factory Method , mas o Effective Java demonstra que o singleton feito dessa forma é um objeto com muitos problemas técnicos (os singletons de verdade do java são controlados pela JVM, na realidade são “prespectivas” da JVM … ). Sobretudo se entrar serialização no barulho. A melhor forma atualmente para escreve um singleton é usar enum. Isso garante não apenas que ha uma instancia,como elimina um monte de problemas relativos a serilização e classloading.

Contudo, como disse, a melhor forma de implementar um singleton é não implementar.

O singleton é um padrão acadêmico que é usado em aula para estabelecer conceitos como static e “numero de instancias”, mas no mundo real ele não é muito prático e na realidade é muito complexo de implementar corretamente

Concordo com Sergio. Singletons não são mais necessários em java pois a maioria dos casos pode ser resolvida com a Injeção de Dependência. Porém acho importante a aprendizagem dos padrões pois podem ser uteis em outras linguagens. Eu mesmo tenho alguns projetos em Delphi no qual uso Singletons devido a limitações da linguagem.

Singleton em java não combina com programação concorrente (threads) e possui alguns problemas de segurança devido a JVM que são muito bem esclarecidos no livro Java Efetivo, então realmente é melhor não utilizar!

E

Pessoal, por que em um singleton que usa “trava dupla”, a variável do tipo da classe tem que usar a palavra reservada volatile?
Que eu saiba, essa palavra é utiliza quando não se deseja persistir a informação.

public class DoubleCheckedLockingSingleton{
     private volatile DoubleCheckedLockingSingleton INSTANCE;
  
     private DoubleCheckedLockingSingleton(){}
  
     public DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}
S

SE usar o volatile sim, mas apenas se usar java 5 ou superior. volatile com java 4 não vai funcionar na mesma…
E se usa java 5 ou superior, enum é melhor que volatile , portanto, use enum e esqueça double checked locking.

No java 4 e antes, onde não pode usar enum, o método sincronizado é a solução correta.

X
ECO2004:
Pessoal, por que em um singleton que usa "trava dupla", a variável do tipo da classe tem que usar a palavra reservada volatile? Que eu saiba, essa palavra é utiliza quando não se deseja persistir a informação.
public class DoubleCheckedLockingSingleton{
     private volatile DoubleCheckedLockingSingleton INSTANCE;
  
     private DoubleCheckedLockingSingleton(){}
  
     public DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

Só lembrando que isso é necessário apenas em um ambiente multi thread! Para aplicações com somente uma thread não necessário todo esse trabalho.

S

ECO2004:
Pessoal, por que em um singleton que usa “trava dupla”, a variável do tipo da classe tem que usar a palavra reservada volatile?
Que eu saiba, essa palavra é utiliza quando não se deseja persistir a informação.

Vc está confundindo com transient. Valatile significa que é seguro duas threads acessarem a variável concorrentemente. Contudo havia problemas com essa definição na especificação da jvm pré 5, o que levava cada vendedor a implementar diferente e não garantia o propósito que se pretende. Apenas no java 5 e depois, onde a especificação foi modificada para não ser ambigua, que a palavra tem alguma utilidade. Contudo no java 5 vieram as api do java.util.concurrent e os enuns, que eliminaram a necessidade de usar essa palavras explicitamente.

E

sergiotaborda:
ECO2004:
Pessoal, por que em um singleton que usa “trava dupla”, a variável do tipo da classe tem que usar a palavra reservada volatile?
Que eu saiba, essa palavra é utiliza quando não se deseja persistir a informação.

Vc está confundindo com transient. Valatile significa que é seguro duas threads acessarem a variável concorrentemente. Contudo havia problemas com essa definição na especificação da jvm pré 5, o que levava cada vendedor a implementar diferente e não garantia o propósito que se pretende. Apenas no java 5 e depois, onde a especificação foi modificada para não ser ambigua, que a palavra tem alguma utilidade. Contudo no java 5 vieram as api do java.util.concurrent e os enuns, que eliminaram a necessidade de usar essa palavras explicitamente.

Muito obrigado.

Realmente eu tava confundindo!

E

Se eu não usar volatile, o que pode acontecer?
Para mim não haveria mudança, pois a criação do objeto está protegida em um bloco sincronizado.

V

@sergiotaborda correto, eu também desencorajo o uso do singleton, mas como um problema para explicar concorrência, double checked locking tem seu valor.

S

Como exercicio académico apenas. Mas lembra do Waterfall ? Também era apenas um exercicio acadêmico para explicar “como não fazer” e virou padrão de uso. O sigleton com DCL seguiu mesmo caminho.
Sempre deve existir o aviso “Manipule com cuidado: Destinado a fins Acadêmicos” :slight_smile:

S

ECO2004:
Se eu não usar volatile, o que pode acontecer?
Para mim não haveria mudança, pois a criação do objeto está protegida em um bloco sincronizado.

A criação sim, mas o primeiro if que checa se existe, não. E é ali que está o problema. Sem o volatile não é garantido que quando a primeira thread seta a variável no bloco de construção, a segunda leia esse valor no if.
O volatile força o “acesso sequencial” à variável, o que significa que quando a segunda thread for ler, ela irá ler o valor correto que a primeira colocou lá.

X

Como exercicio académico apenas. Mas lembra do Waterfall ? Também era apenas um exercicio acadêmico para explicar “como não fazer” e virou padrão de uso. O sigleton com DCL seguiu mesmo caminho.
Sempre deve existir o aviso “Manipule com cuidado: Destinado a fins Acadêmicos” :)

Não acredito que singletons se aplique somente a fins acadêmicos. No java realmente não faz mais sentido usa-los pois existe a Injeção de Dependência, porém em outras linguagens o uso pode ser valido! Mas sempre com moderação! Singletons não é algo que se deva ficar espalhando pelo projeto!

S

Como exercicio académico apenas. Mas lembra do Waterfall ? Também era apenas um exercicio acadêmico para explicar “como não fazer” e virou padrão de uso. O sigleton com DCL seguiu mesmo caminho.
Sempre deve existir o aviso “Manipule com cuidado: Destinado a fins Acadêmicos” :)

Não acredito que singletons se aplique somente a fins acadêmicos. No java realmente não faz mais sentido usa-los pois existe a Injeção de Dependência, porém em outras linguagens o uso pode ser valido! Mas sempre com moderação! Singletons não é algo que se deva ficar espalhando pelo projeto!

concordo com vc. o termo "Acadêmico’ referia-se ao "double checked locking " e não ao singleton em si.

E

sergiotaborda:
ECO2004:
Se eu não usar volatile, o que pode acontecer?
Para mim não haveria mudança, pois a criação do objeto está protegida em um bloco sincronizado.

A criação sim, mas o primeiro if que checa se existe, não. E é ali que está o problema. Sem o volatile não é garantido que quando a primeira thread seta a variável no bloco de construção, a segunda leia esse valor no if.
O volatile força o “acesso sequencial” à variável, o que significa que quando a segunda thread for ler, ela irá ler o valor correto que a primeira colocou lá.

Valeu pela explicação!

V

ECO2004:
Pessoal, por que em um singleton que usa “trava dupla”, a variável do tipo da classe tem que usar a palavra reservada volatile?
Que eu saiba, essa palavra é utiliza quando não se deseja persistir a informação.

O double checked locking não só deixa o código extremamente complicado, como também não funciona:


http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html (escrito pelo Brian Goetz, criador da linguagem)

J

Na minha opinião a melhor maneira de se usar um singleton é essa:

public enum Age {
    INSTANCE;
    private int age;

    public int getAge() {
        return age;
    }
}

Simples e rápido, mas você não consegue recuperar .class.

E
juliocbq:
Na minha opinião a melhor maneira de se usar um singleton é essa:
public enum Age {
    INSTANCE;
    private int age;

    public int getAge() {
        return age;
    }
}

Simples e rápido, mas você não consegue recuperar .class.

Eu não sei usar o enum como singleton. Como ficaria o seu uso? Como eu retornaria apenas um objeto com o enum?

S
ECO2004:
juliocbq:
Na minha opinião a melhor maneira de se usar um singleton é essa:
public enum Age {
    INSTANCE;
    private int age;

    public int getAge() {
        return age;
    }
}

Simples e rápido, mas você não consegue recuperar .class.

Eu não sei usar o enum como singleton. Como ficaria o seu uso? Como eu retornaria apenas um objeto com o enum?

:shock: :shock:

// obtem instancia
Age age = Age.INSTANCE;
// usa a instancia
age.getAge()

Simples.

E
sergiotaborda:
ECO2004:
juliocbq:
Na minha opinião a melhor maneira de se usar um singleton é essa:
public enum Age {
    INSTANCE;
    private int age;

    public int getAge() {
        return age;
    }
}

Simples e rápido, mas você não consegue recuperar .class.

Eu não sei usar o enum como singleton. Como ficaria o seu uso? Como eu retornaria apenas um objeto com o enum?

:shock: :shock:

// obtem instancia
Age age = Age.INSTANCE;
// usa a instancia
age.getAge()

Simples.

Está me parecendo que está faltando algo. Pelo que sei de enum, quando invoco uma constante de enum, o seu construtor é chamado. Essa chamada ao construtor é segura. Para mim, falta nesse enum o objeto ao qual se deseja ter apenas uma instância, não é?

J
ECO2004:
sergiotaborda:
ECO2004:
juliocbq:
Na minha opinião a melhor maneira de se usar um singleton é essa:
public enum Age {
    INSTANCE;
    private int age;

    public int getAge() {
        return age;
    }
}

Simples e rápido, mas você não consegue recuperar .class.

Eu não sei usar o enum como singleton. Como ficaria o seu uso? Como eu retornaria apenas um objeto com o enum?

:shock: :shock:

// obtem instancia
Age age = Age.INSTANCE;
// usa a instancia
age.getAge()

Simples.

Está me parecendo que está faltando algo. Pelo que sei de enum, quando invoco uma constante de enum, o seu construtor é chamado. Essa chamada ao construtor é segura. Para mim, falta nesse enum o objeto ao qual se deseja ter apenas uma instância, não é?

Não falta nada. Usa ele diretamente. Como é singleton você não precisa ficar passando suas instâncias em outras referências.

Age.INSTANCE.getAge();
E
juliocbq:
ECO2004:
sergiotaborda:
ECO2004:
juliocbq:
Na minha opinião a melhor maneira de se usar um singleton é essa:
public enum Age {
    INSTANCE;
    private int age;

    public int getAge() {
        return age;
    }
}

Simples e rápido, mas você não consegue recuperar .class.

Eu não sei usar o enum como singleton. Como ficaria o seu uso? Como eu retornaria apenas um objeto com o enum?

:shock: :shock:

// obtem instancia
Age age = Age.INSTANCE;
// usa a instancia
age.getAge()

Simples.

Está me parecendo que está faltando algo. Pelo que sei de enum, quando invoco uma constante de enum, o seu construtor é chamado. Essa chamada ao construtor é segura. Para mim, falta nesse enum o objeto ao qual se deseja ter apenas uma instância, não é?

Não falta nada. Usa ele diretamente. Como é singleton você não precisa ficar passando suas instâncias em outras referências.

Age.INSTANCE.getAge();

Com esse seu exemplo, qual é o objeto singleton? Em outras palavras, qual é o objeto que é único na memória?

X

Sempre que vejo uma implementação de um singleton utilizando o enum acho muiiiitooo extranho… rsrsrs

Sei os motivos de segurança para isso mas, como eu venho de outras linguagens aprendi, que enum é somente um conjunto finito de elementos! Java transformou isso em uma “classe” o que é ótimo pois em outras linguagens, por exemplo, se você queria associar um “Nome” a um valor do enumerador, tinha que fazer uma função só para isso. Mas vendo ele sendo utilizado como singleton é demais :smiley:

Java sempre me surpreende, permite coisas simplesmente incriveis… só perde para C é claro! :smiley: rsrsrsrsr

X

INSTANCE é unico. Age.INSTANCE é “similar” a chamada um campo publico e estático de uma classe. Note que eu disse similar e não igual, existem coisas sendo feitas nos bastidores!

Você pode entender, nesse caso, que INSTANCE representa a própria classe Age!
O exemplo com enumerador seria parecido com isso, mas usando enum você não tem os problemas já citados de concorrência e segurança!

public class Age {
  
  private Age(){
  }
  
  public static Age INSTANCE = new Age();
  
  public int getAge(){
    return 1
  }
}
J
x@ndy:
ECO2004:
Age.INSTANCE.getAge();
Com esse seu exemplo, qual é o objeto singleton? Em outras palavras, qual é o objeto que é único na memória?
INSTANCE é unico. Age.INSTANCE é "similar" a chamada um campo publico e estático de uma classe. Note que eu disse similar e não igual, existem coisas sendo feitas nos bastidores! Você pode entender, nesse caso, que INSTANCE representa a própria classe Age! O exemplo com enumerador seria parecido com isso, mas usando enum você não tem os problemas já citados de concorrência e segurança!
public class Age {
  
  private Age(){
  }
  
  public static Age INSTANCE = new Age();
  
  public int getAge(){
    return 1
  }
}

Isso mesmo. INSTANCE aponta pro próprio enum com a vantagem de ser uma chamada estática, o que o torna um ótimo singleton.

X

juliocbq:

Isso mesmo. INSTANCE aponta pro próprio enum com a vantagem de ser uma chamada estática, o que o torna um ótimo singleton.

Na verdade INSTANCE é INSTANCE! Ele é um elemento do enumerador mas não é o enumerador! Ele também não uma classe, embora tenha comportamento de classe!

O enum pode ser “abstraido” como um conjunto, ou um vetor, com elementos, mas os elementos pertencentes ao conjunto não representam o próprio conjunto.

Por isso acho é muiito esquisito! :smiley:

E
juliocbq:
x@ndy:
ECO2004:
Age.INSTANCE.getAge();
Com esse seu exemplo, qual é o objeto singleton? Em outras palavras, qual é o objeto que é único na memória?
INSTANCE é unico. Age.INSTANCE é "similar" a chamada um campo publico e estático de uma classe. Note que eu disse similar e não igual, existem coisas sendo feitas nos bastidores! Você pode entender, nesse caso, que INSTANCE representa a própria classe Age! O exemplo com enumerador seria parecido com isso, mas usando enum você não tem os problemas já citados de concorrência e segurança!
public class Age {
  
  private Age(){
  }
  
  public static Age INSTANCE = new Age();
  
  public int getAge(){
    return 1
  }
}

Isso mesmo. INSTANCE aponta pro próprio enum com a vantagem de ser uma chamada estática, o que o torna um ótimo singleton.

Quer dizer que se eu quiser utilizar o padrão singleton e enum ao mesmo tempo, tenho que transformar a classe o qual desejo ter apenas um objeto em memória em um enum?

Isso que eu não estava entendendo...não estava vendo qualquer referência a uma classe (além de enum).

E

x@ndy:
juliocbq:

Isso mesmo. INSTANCE aponta pro próprio enum com a vantagem de ser uma chamada estática, o que o torna um ótimo singleton.

Na verdade INSTANCE é INSTANCE! Ele é um elemento do enumerador mas não é o enumerador! Ele também não uma classe, embora tenha comportamento de classe!

O enum pode ser “abstraido” como um conjunto, ou um vetor, com elementos, mas os elementos pertencentes ao conjunto não representam o próprio conjunto.

Por isso acho é muiito esquisito! :D

Que eu saiba, enum é uma classe com algumas restrições, como ter um construtor que não tem qualquer modificador de acesso, sequer public. Cada constante de um enum é um objeto. A chamada de uma constante chama o construtor desse enum. A chamada de uma constante de enum é seguro para threads.

S

Este seu conceito está errado. Voc~e realmente precisa conhecer melhor a linguagem antes de se aventurar nos patterns. Senão vai perder muitas nuances.

Quando vc declara um enum, o construtor é chamado pela JVM garantidamente apenas uma vez durante a inicialização da classe ( os campos do enum são automaticamente estáticos e públicos)

É a garantia de ser chaamdo apenas 1 vez mesmo que vc use serialização ou mutliplos classloaders que dá ao enum os poderes necessários para ser um bom singleton. Dito de outra forma, a JVM garante que cada instancia das classes do tipo enum são singletons.
Então, para ter o padrão singleton basta declarar um enum com um único elemento ( e automagicamente ele será um singleton pela estrutura do enum)

Enum não é um padrão em si. Não existe isso de “usar o padrão singleton e o padrão enum ao mesmo tempo”.
Enum é uma forma especial de tipo ( as outras são Classe, Interface e Annotation)

Não ha problema nenhum em uma enumeração só ter um elemento (conjunto singular). Aliás essa é a definição do que significa ser singleton ( único no conjunto)

X
ECO2004:
Quer dizer que se eu quiser utilizar o padrão singleton e enum ao mesmo tempo, tenho que transformar a classe o qual desejo ter apenas um objeto em memória em um enum? Isso que eu não estava entendendo...não estava vendo qualquer referência a uma classe (além de enum).
Enum não um "Padrão de Projeto". Basicamente enumeradores são conjuntos finitos de dados. Por exemplo eu posso criar um enum em java para os dias da semana que seria
enum DiasDaSemana {
  SEGUNDA,
  TERCA,
  QUARTA,
  QUINTA,
  SEXTA,
  SABADO,
  DOMINGO;
}
Enumeradores são basicamente isso em qualquer linguagem! Você utiliza normalmente na classificação de um tipo de dados, por exempo, se você tem uma classe chamada Dia, poderia utilizar o enum DiasDaSemana para classifica-la. Porém, em java enumeradores tem comportamento de classe! Você pode definir métodos para eles como no exemplo abaixo:
enum DiasDaSemana {
  SEGUNDA("Segunda Feira"),
  TERCA("Terça Feira"),
  QUARTA("Quarta Feira"),
  QUINTA("Quinta Feira"),
  SEXTA("Sexta Feira"),
  SABADO("Sabado"),
  DOMINGO("Domingo");

  private String nomeDoDia;

  public DiasDaSemana (String nomeDoDia){
    this.nomeDoDia = nomeDoDia;
  }
  
  public string nomeDoDia (){
    return this.nomeDoDia;
  }
}
Quando você cria uma variável para esse enumerador e atribui a ela um dos valores da enumeração a jvm cria para você um único item, que será compartilhado por todas as referencias! Assim você tem sempre um único item da enumeração 'instanciado' sempre. Isso faz com que ele seja ótimo para singletons. enums também já foram projetados para serem a prova de threads

PS: Uma coisa que tem que ficar clara é que enumeradores não foram feitos para serem usados como Singletons. Eles são usados por conveniência devido a problemas de segurança e a acesso por theads! Eu, como já comentei, acho estranho até o uso para isso e num mundo ideal com certeza não usaria enumeradores para criar singletons!

S

x@ndy:

PS: Uma coisa que tem que ficar clara é que enumeradores não foram feitos para serem usados como Singletons. Eles são usados por conveniência devido a problemas de segurança e a acesso por theads! Eu, como já comentei, acho estranho até o uso para isso e num mundo ideal com certeza não usaria enumeradores para criar singletons!

então vc precisa ler o Effective Java. Não usar enum é puro suicídio.
Enum foram feitos com uma serie de salvaguardas porque cada instancia do enum tem que ser única (Shared Object). Pro exemplo se vc serializar e desserializr o enum e der == vai dar true. Isto porque a JVM garante esta únicidade.
Mas esta unicidade é exactamente o que precisa para criar um singleton corretamente. O Enum não foi criado explicitamente para implementar singleton , mas é o artefacto tecnico melhor para realizar essa tarefa.

Obviamente o enum não tem que ser publico. Acho que é isso que está confundindo vcs. Mas é a forma mais segura, eficiente e pragmática de fazer. Então, porque fazer diferente ?

J

sergiotaborda:
x@ndy:

PS: Uma coisa que tem que ficar clara é que enumeradores não foram feitos para serem usados como Singletons. Eles são usados por conveniência devido a problemas de segurança e a acesso por theads! Eu, como já comentei, acho estranho até o uso para isso e num mundo ideal com certeza não usaria enumeradores para criar singletons!

então vc precisa ler o Effective Java. Não usar enum é puro suicídio.
Enum foram feitos com uma serie de salvaguardas porque cada instancia do enum tem que ser única (Shared Object). Pro exemplo se vc serializar e desserializr o enum e der == vai dar true. Isto porque a JVM garante esta únicidade.
Mas esta unicidade é exactamente o que precisa para criar um singleton corretamente. O Enum não foi criado explicitamente para implementar singleton , mas é o artefacto tecnico melhor para realizar essa tarefa.

Obviamente o enum não tem que ser publico. Acho que é isso que está confundindo vcs. Mas é a forma mais segura, eficiente e pragmática de fazer. Então, porque fazer diferente ?

Foi justamente lendo esse livro que passei a usar enums como singleton.

X

sergiotaborda:
x@ndy:

PS: Uma coisa que tem que ficar clara é que enumeradores não foram feitos para serem usados como Singletons. Eles são usados por conveniência devido a problemas de segurança e a acesso por theads! Eu, como já comentei, acho estranho até o uso para isso e num mundo ideal com certeza não usaria enumeradores para criar singletons!

então vc precisa ler o Effective Java. Não usar enum é puro suicídio.
Enum foram feitos com uma serie de salvaguardas porque cada instancia do enum tem que ser única (Shared Object). Pro exemplo se vc serializar e desserializr o enum e der == vai dar true. Isto porque a JVM garante esta únicidade.
Mas esta unicidade é exactamente o que precisa para criar um singleton corretamente. O Enum não foi criado explicitamente para implementar singleton , mas é o artefacto tecnico melhor para realizar essa tarefa.

Obviamente o enum não tem que ser publico. Acho que é isso que está confundindo vcs. Mas é a forma mais segura, eficiente e pragmática de fazer. Então, porque fazer diferente ?


Sergio, e li o Efective Java (e recomendo que todo mundo leia) mas acho que você não entendeu a minha colocação!
Estou dizendo que enum foi criado para ser um enumerador como em qualquer outra linguagem, mas mais ‘evoluído’ por assim dizer! Enumeradores era algo que linguagem deveria ter desde o inicio e sua falta criou uma série de problemas com o uso de constantes, fato que o Joshua aborda no Efective Java!

Só que enumeradores não foram feitos pensando no problema do padrão singleton no java! Essa não é sua principal finalidade e isso tem que ficar claro! O uso de enum para implementar um singleton é uma casualidade! Seu uso se deve justamente pelo que tu colocaste acima, pois se fosse seguro e simples usar singletons em java sem enum, através da implementação de uma classe, essa seria a melhor opção. Mas o mundo não perfeito e java não é perfeito então usar enum para implementar um singleton é um mal necessário!

J

x@ndy:
sergiotaborda:
x@ndy:

PS: Uma coisa que tem que ficar clara é que enumeradores não foram feitos para serem usados como Singletons. Eles são usados por conveniência devido a problemas de segurança e a acesso por theads! Eu, como já comentei, acho estranho até o uso para isso e num mundo ideal com certeza não usaria enumeradores para criar singletons!

então vc precisa ler o Effective Java. Não usar enum é puro suicídio.
Enum foram feitos com uma serie de salvaguardas porque cada instancia do enum tem que ser única (Shared Object). Pro exemplo se vc serializar e desserializr o enum e der == vai dar true. Isto porque a JVM garante esta únicidade.
Mas esta unicidade é exactamente o que precisa para criar um singleton corretamente. O Enum não foi criado explicitamente para implementar singleton , mas é o artefacto tecnico melhor para realizar essa tarefa.

Obviamente o enum não tem que ser publico. Acho que é isso que está confundindo vcs. Mas é a forma mais segura, eficiente e pragmática de fazer. Então, porque fazer diferente ?


Sergio, e li o Efective Java (e recomendo que todo mundo leia) mas acho que você não entendeu a minha colocação!
Estou dizendo que enum foi criado para ser um enumerador como em qualquer outra linguagem, mas mais ‘evoluído’ por assim dizer! Enumeradores era algo que linguagem deveria ter desde o inicio e sua falta criou uma série de problemas com o uso de constantes, fato que o Joshua aborda no Efective Java!

Só que enumeradores não foram feitos pensando no problema do padrão singleton no java! Essa não é sua principal finalidade e isso tem que ficar claro! O uso de enum para implementar um singleton é uma casualidade! Seu uso se deve justamente pelo que tu colocaste acima, pois se fosse seguro e simples usar singletons em java sem enum, através da implementação de uma classe, essa seria a melhor opção. Mas o mundo não perfeito e java não é perfeito então usar enum para implementar um singleton é um mal necessário!

Eu não chamaria de mal não. Chamaria de “solução bem colocada”.

Como programar em c++ e precisar de uma função para resolver determinados tipos de problemas. No java se usaria uma “classe helper”, mas em c++ esse conceito cai por terra porque “se você escreve uma classe para apenas prover uma função, você não precisa da classe em si, mas de uma função top level”.

Aí vem aquele monte de críticas dizendo sobre conceitos OO etc… Mas o top Level é a melhor solução nesse caso.

X

Isso realmente é um problema! Só que é dificil para quem está iniciando se aprofundar na linguagem e somente depois apreender os padrões! Acredito que os métodos de ensino deveriam começar a ensinar utilizando padrões! Quando alguém fosse falar de métodos e campos estáticos, por exemplo, poderia usa os padrões Factory Method e o próprio Singleton para isso! Estou justamente pensando em escrever um livro de introdução a java abordando esses aspectos, mas falta tempo.

sergiotaborda:

Não ha problema nenhum em uma enumeração só ter um elemento (conjunto singular). Aliás essa é a definição do que significa ser singleton ( único no conjunto)

Aqui tu pegou pesado Sergio :smiley: Com certeza não existe problema em ter um enum com unico elemento (tirando o fato de ser estranho) mas um singleton é único no conjunto de objetos da aplicação e não do de itens do Enum. Tanto que posso ter um enum com varios itens os quais serão sempre únicos no contexto da aplicação :smiley:

X

É isso que estou colocando como mal necessário! Para mim as funções top level também entram nesse quesito, pois é mais simples, fácil e eficiente embora não seja o ideal! Como disse não existe linguagem perfeita e nem paradigmas perfeitos, então é muitas vezes temos que contornar os problemas da melhor forma :slight_smile:

Criado 27 de fevereiro de 2013
Ultima resposta 28 de fev. de 2013
Respostas 45
Participantes 10