Design Patterns ou padrões de projetos são soluções generalistas para problemas recorrentes durante o desenvolvimento de um software. Não se trata de um framework ou um código pronto, mas de uma definição de alto nível de como um problema comum pode ser solucionado. Conhecer Design Patterns é algo de extrema importância no desenvolvimento de qualquer software. A utilização desses padrões nos ajuda a desenvolver de forma mais rápida frente a desafios semelhantes. Neste artigo vamos falar especificamente sobre o Singleton Design Pattern.
Apesar de existir uma certa polêmica em torno do Singleton (alguns o consideram como anti pattern), veremos que ele pode ser muito útil se usado da maneira correta.
Singleton especifica que apenas uma instância da classe pode existir, e esta será utilizada por toda a aplicação. Dessa forma temos apenas um ponto de acesso central a esta instância da classe.
Vantagens do Singleton Design Pattern
Ao utilizar Singleton Design Pattern temos mais controle sobre o acesso às propriedades e métodos de uma classe, e também reduzimos o consumo de memória desnecessário por utilizar várias instancias desnecessárias de uma classe.
“Reduz o consumo de memória? Então tenho que usar Singleton sempre!”
NÃO!
Vamos com calma.
Apesar da frase “reduzir o consumo de memória” chamar a atenção, nem sempre isso acontece. Deve-se levar em consideração que a redução do consumo de memória ocorre no contexto de não termos instâncias desnecessárias de uma classe que é utilizada com frequência por toda a aplicação. Uma implementação incorreta desse padrão poderia ocasionar um desperdício de memória se o seu Singleton for utilizado raramente, já que você terá uma instância de um objeto desnecessário ocupando a memória da máquina.
>> Leitura recomendada: Design Patterns – O que são e quais os benefícios?
Onde é recomendada a utilização de Singleton Design Pattern?
Singleton deve ser utilizado com cautela, pois nem sempre é útil ter apenas uma instância de uma classe sendo compartilhada por toda a aplicação.
Recomendo utilizar o Singleton Design Pattern:
- Quando você precisar controlar a concorrência de acesso a recursos compartilhados;
- Quando uma classe for utilizada com frequência por várias partes distintas do sistema, e essa classe não gerencia nenhum estado da aplicação;
Log
Logs normalmente são utilizados por quase todas as classes de um sistema, e não retornam nenhuma informação que afeta o comportamento da aplicação. Este é um caso no qual o singleton pode ser bem empregado.
Variáveis de configuração
Variáveis de configuração podem ser carregadas ao iniciar o sistema, e normalmente não são alteradas diretamente pela aplicação. Utilizar uma classe singleton para armazenar essas informações pode funcionar como um cache em memória para que não seja necessário “buscar” essa informação toda vez que uma delas for requisitada.
Acessar recursos de hardware compartilhado
Uma aplicação multi-thread que necessite acessar recursos de um hardware onde o mesmo não foi feito para trabalhar dessa forma pode se beneficiar desse padrão. Isso porque todas as classes que necessitarem acessar algum recurso do hardware deverão utilizar a mesma instância da classe singleton. Esta será responsável por controlar todas as operações de comunicação entre a aplicação e o hardware.
Como criar uma classe Singleton?
A forma mais simples de criar um objeto Singleton é criando uma classe que possui um construtor privado e uma instância estática dela mesma.
Para utilizarmos essa instância estática criamos um método que verifica se a classe já foi instanciada. Se sim retornamos ela, caso contrário criamos uma e retornamos.
Você pode testar essa utilização neste link, onde você pode visualizar o código fonte, executar no próprio browser e ver como as suas alterações impactam a execução do código.
Implementações de Singleton Design Pattern no mercado
Você não precisa utilizar uma implementação manual do singleton sempre que (raramente) precisar dele. Muitos frameworks e biliotecas já controlam isso para você. Alguns exemplos do mundo .Net são:
MEF (Managed Extensibility Framework)
O MEF ou Managed Extensibility Framework, como o próprio nome já diz é um framework que permite gerenciar a forma como uma aplicação pode ser estendida ou customizada através dele. Toda classe que é importada via MEF é por padrão uma implementação Singleton dessa classe. Caso você queira utilizar o MEF e deseja que cada import de uma classe crie uma nova instância dessa classe, então será necessário deixar isso explícito na configuração de export da classe. Caso contrário sua classe terá uma única instância compartilhada por toda a aplicação. Para saber mais sobre o MEF, recomendo a leitura desse artigo no site da Microsoft.
ASP.Net Core
O ASP.Net core é uma versão totalmente nova do antigo Asp.Net, que foi reescrita do zero para trazer uma série de melhorias para nossas aplicações. Uma delas é trabalhar nativamente com Injeção de Dependência, um outro Design Pattern. No ASP.Net Core, ao configurar a injeção de uma dependência no projeto, você deve deixar explícito como ela será resolvida quando alguém precisar dela. O framework faz isso através da classe IServiceCollection, que provê três métodos:
AddTransient: Esse método garante que sempre que alguma parte do seu código precisar dessa dependência, uma nova instância será criada.
AddScoped: Garante que a mesma instância de uma classe será compartilhada em cada request. Ou seja, cada request terá uma única instância deste objeto.
AddSingleton: Como o próprio nome diz, cria uma instância única da classe que será compartilhada em todos os objetos que a solicitarem, e será a mesma instância mesmo em requests diferentes.
Conclusão
Sempre que falo sobre Design Patterns, gosto de lembrar que não existe bala de prata. Devemos sempre analisar cada cenário antes de escolhermos utilizar ou não um padrão de projeto. Com o singleton, cabe uma análise especial, pois uma implementação errada dele pode levar sua aplicação a compartilhar informações entre objetos que não deveriam ser compartilhadas.
Portanto, entenda bem o cenário onde você pretende utilizá-lo e, principalmente, entenda bem como ele funciona. Desenvolva a suas próprias provas de conceito e analise o resultado obtido antes de mais nada. Sempre tenha em mente que conhecer bem os Design Patterns e suas aplicações pode te economizar algumas horas de desenvolvimento.