Padrão de projeto – parte 3: Singleton
Design Pattern: Singleton
Definição segundo GoF: Garantir que uma classe só tenha uma única instância, e prover um ponto de acesso global a ela.
Exemplo baseado no mundo real: Se você tem um objeto que é uma montadora de carros onde sua responsabilidade é criar vários objetos carros. Existe a necessidade de criar a instância de uma nova montadora a cada vez que for criar um novo carro? A resposta é simples: iniciamos o sistema, criamos uma única instância da montadora, quando necessário (isto chama-se instancialização preguiçosa), e usamos ela para criar todos os carros necessários.
Peraí?! Isto não é a mesma coisa que criar uma variável global e utilizá-la? Não! O singleton é usado justamente para não ter as famosas variáveis globais. Ao invés de ficar criando variáveis estáticas pelo sistema, acessando de tudo que é forma, criamos uma classe, garantimos que só vai existir uma única instância dela em memória e criamos um único ponto de acesso para pegar sua instância.
Aplicações:
- Um objeto único de acesso a banco de dados;
- Um objeto único de acesso a um arquivo de log;
- Um objeto único de acesso a configurações gerais do sistema;
- Um objeto único que representa um vídeo usado inúmeras vezes no sistema;
- Uma objeto facade (fachada) único. Este é um pattern que será visto mais adiante.
Vantagens:
- Não ter variáveis globais sendo acessadas dentro do software;
- Ter sempre um único local responsável por uma informação estática (instância da classe) (static);
- Organizar a modelagem e codificação em classes;
Desvantagens:
- Qualidade da implementação depende da linguagem;
- Não é tão simples controlar em ambiente distribuído;
Observações: Cuidado na implementação multi-thread. Veja os exemplos de códigos abaixo.
Diagrama de classe:
Implementação em C#:
public class Singleton
{
private static Singleton uniqueInstance;
//-- Outras variáveis de instância úteis aqui
private Singleton() { } //-- Não pode ser instanciado
public static Singleton GetInstance()
{
if (uniqueInstance == null)
uniqueInstance = new Singleton();
return uniqueInstance;
}
//-- Outros métodos úteis aqui
}
Close no código:
Implementação comum em C# thread-safe (multi-threads) sem instancialização preguiçosa:
public class Singleton
{
private static Singleton uniqueInstance = new Singleton();
//-- Outras variáveis de instância úteis aqui
private Singleton() { } //-- Não pode ser instanciado
public static Singleton GetInstance()
{
return uniqueInstance;
}
//-- Outros métodos úteis aqui
}
Implementação em C# thread-safe (multi-threads) com instancialização preguiçosa, variável volatile e lock:
public class Singleton
{
private static object sync = new object();
private static volatile Singleton uniqueInstance;
//-- Outras variáveis de instância úteis aqui
private Singleton() { } //-- Não pode ser instanciado
public static Singleton GetInstance()
{
if (uniqueInstance == null)
{
lock (sync)
{
if (uniqueInstance == null)
uniqueInstance = new Singleton();
}
}
return uniqueInstance;
}
//-- Outros métodos úteis aqui
}
Com o modificador volatile você garante que, independente do processo que esteja acessando a váriavel/propriedade, ele pegará o valor atual da váriavel/propriedade. Um post sobre modificadores virá mais a frente.
Chamada da classe Singleton em C#:
Singleton sing = Singleton.GetInstance();
Os exemplos em C# são apenas demonstrativos, se alguêm quiser enviar exemplos em outras linguagens eu altero o post e coloco o respectivo nome do autor
.
Abrs a todos e até a próxima.
“Em questões de estilo, nade contra corrente; em questões de princípios, fique firme como uma rocha.” Thomas Jefferson


Olá lorival,
eu não entendi exatamente porque vc declarol um atributo do tipo Object e bloqueou ele no método getInstance.
Eu pensei que deveria usar o lock(this) para bloquear a fachada?
Poderia explicar isso melhor.
Obrigado!
Olá,
Tudo bem? Peço desculpas por não ter respondido antes seu comentário… tenho que fazer a faxina por aqui, tá muito abandonado.
O lock você pode aplicar a instância da classe que você está [ é o que o lock(this) faz ] ou aqualquer outro objeto [ que seja criado por referência, ou seja, não vale int, decimal....].
No exemplo do post, criamos um objeto genérico [ chamado sync do tipo object para ser genérico ] que serve como um semáforo [ podes ver mais sobre isto dentro de Sistemas Operacionais ], onde bloqueamos apenas o acesso a aquele objeto [ quando necessário, naquele caso na criação ]. Ao utilizar o lock(this) estariamos bloqueando toda instância da objeto representado pela classe.
Assim, com threads concorrentes, criamos um semáforo [ sync ] e definimos como vermelho [ lock(sync) ], ninguém passa enquanto o semáforo [ sync ] estiver bloqueado e assim podemos criar a instância do objeto singleton sem problemas e a instância da classe está livre para ser utilizada por qualquer outro método se necessário.
Abrs,