Padrão de Projeto: Singleton
(12299)
CategoriasAndroid, Design, Protótipo
AutorVinÃcius Thiengo
VÃdeo aulas186
Tempo15 horas
ExercÃciosSim
CertificadoSim
CategoriaEngenharia de Software
Autor(es)Vaughn Vernon
EditoraAlta Books
Edição1ª
Ano2024
Páginas160
Tudo bem?
Neste artigo vamos abordar o padrão de projeto Singleton.
Simples e, com segurança, o padrão mais utilizado.
Ele também é pré-requisito para a explicação de dois outros métodos de refatoração, são eles:
Antes de prosseguir, não esqueça de se inscrever na 📫 lista de e-mails do Blog para receber em primeira mão todos os conteúdos exclusivos sobre desenvolvimento e codificação limpa.
A seguir os tópicos abordados neste artigo:
Apresentação
Vamos iniciar com a definição do Singleton:
Permitir que a classe tenha somente uma instância no projeto e que essa instância seja de acesso global.
Esse padrão é muito utilizado quando o algoritmo trabalha com instâncias de classes que não alteram seus estados ou quando há necessidade de utilizar algum método especifico da classe, várias vezes.
Algo que seria um problema sem o Singleton.
Muitas instâncias seriam criadas somente para utilizar alguns métodos, esse sendo independentes do estado do objeto.
Você pode estar se perguntando: O que você quer dizer com "... não alteram seus estados"?
Isso significa que os atributos (equivalente a estados da instância) da classe que implementa o Singleton, ou não são alterados depois de já terem sido iniciados ou, se tiver alteração, essa não impacta para diferenciar o comportamento de mais instâncias da classe caso não houvesse o Singleton.
Com o exemplo no artigo ficará mais simples de entender.
Um código utilizando o Singleton explica muito mais.
Mas primeiro vamos a diagrama difícil desse padrão.
Diagrama
É isso mesmo, apenas uma classe descrevendo o padrão. O +... em atributos indica possíveis outros atributos e +...() possíveis outros métodos. instanciaUnica e getInstancia() são sempre estáticos.
Código de exemplo
Vamos trabalhar a típica classe que não precisa, na maior parte dos casos, de mais de uma instância.
Classe de conexão com banco de dados.
Abaixo a classe BancoDeDados sem implementar o Singleton:
public class BancoDeDados {
...
public BancoDeDados(){
...
}
}
O que nos interessa nesse projeto de classe são as partes que permitem a instanciação da classe BancoDeDados.
Sabendo dos possíveis problemas de ter mais de uma conexão com o banco de dados na mesma execução de código ao mesmo tempo.
Você, desenvolvedor do projeto, se policia para não criar duas instâncias ao mesmo tempo.
Porém essa não é uma escolha segura, pois outros developers podem não ter esse mesmo cuidado.
Devemos então bloquear a possibilidade de instanciar essa classe fora dela mesma (até mesmo subclasses não poderão instanciá-la) e também criar uma maneira de permitir que somente uma instância dela exista.
Nesse contexto, a melhor solução é a aplicação do padrão Singleton.
Modificando o código para:
public class BancoDeDados {
private static BancoDeDados instance;
...
private BancoDeDados(){
...
}
public static BancoDeDados getInstance(){
if( instance == null ){
instance = new BancoDeDados();
}
return instance;
}
...
}
Com isso atingimos o objetivo e códigos clientes ainda conseguem obter uma instância de BancoDeDados por meio do método estático e público, getInstance().
Sempre teremos somente uma instância de BancoDeDados (será?) e a instanciação direta não é possível (o construtor é private agora), descartando de vez a necessidade "me policiar".
Por que disse "será?"?
Porque ainda há um problema.
Caso você trabalhe com Threads, mais de uma solicitando um objeto de BancoDeDados pode ocasionar na geração de mais de uma instância.
No Java é possível resolver esse problema utilizando o que chamamos de "trava duplamente verificada".
O código de BancoDeDados ficaria:
public class BancoDeDados {
private volatile static BancoDeDados instance;
...
private BancoDeDados(){
...
}
public static BancoDeDados getInstance(){
if( instance == null ){
synchronized( BancoDeDados.class ){
if( instance == null ) {
instance = new BancoDeDados();
}
}
}
return instance;
}
...
}
A palavra reservada synchronized permite que somente um processamento por vez acesse o trecho de código dentro do bloco dela, ou seja, um controle maior para quando há processamentos paralelos, Threads, e parte do código deve ser executado no formato "não paralelo".
A palavra reservada volatile tem tarefa similar ao bloco de código de synchronized.
Ela permite que o atributo instance seja trabalhado em código multi-threading de maneira mais eficiente, incluindo a não utilização de cache local na Thread, sabendo que o atributo será compartilhado entre Threads.
Poderíamos trabalhar com apenas uma das soluções, synchronized ou volatile, incluindo a não aplicação redundante da verificação instance == null fora e dentro de synchronized.
Porém, da maneira acima, o código está mais seguro em uma aplicação multi-threading, com ele realmente haverá somente uma instância de BancoDeDados.
Se a linguagem que você utiliza tem a possibilidade de gerenciamento de Threads, provavelmente ela deve ter meios para evitar o problema do Singleton em códigos multi-threading.
Segue um código cliente de BancoDeDados:
public class ClienteBancoDeDados {
public static void main( String args[] ){
BancoDeDados bd = BancoDeDados.getInstance();
...
/* AINDA A MESMA INSTÂNCIA DE bd */
BancoDeDados bdNovo = BancoDeDados.getInstance();
...
}
}
E se eu quiser utilizar tudo estático (classe, métodos e atributos)? Dessa forma sempre terei somente uma instância.
Sim, porém isso não é um Singleton, terá o acesso global, mas não estará evitando a criação de mais de uma instância. E essa não é uma prática recomendável, visto que todo o conteúdo será de acesso global sem uma instância de controle intermediando esse acesso.
Qual o problema do acesso global?
As atualizações de valores.
Com entidades globais você pode perder o controle das atualizações dos valores delas e consequentemente acabar tendo resultados inesperados na saída do software.
Algo comum quando o projeto de software começa a ficar grande (dezenas de milhares de linhas de código).
Uma variação do padrão Singleton, digo, na implementação, é como se segue:
public class BancoDeDados {
private volatile static BancoDeDados instance = new BancoDeDados();
...
private BancoDeDados(){
...
}
public static BancoDeDados getInstance(){
return instance;
}
...
}
A parte negativa no código anterior está na instanciação da classe BancoDeDados mesmo quando não há necessidade de utilizá-la.
Neste caso estaríamos inflando a memória destinada ao aplicativo.
Para sistemas mobile isso é realmente um problema, pois o limite de recursos é muito maior do que em sistemas desktop.
Ponto negativo
- Utilizar a versão de inicialização direta, ainda na declaração da instância Singleton, pode trazer problemas de desempenho se seu software não for utilizar com frequência a classe que implementa o padrão.
Pontos positivos
- Melhora o desempenho do software quando utilizado em classes que nunca mudam de estado ou que sempre precisam de somente uma instância executando;
- Melhora a leitura do código, tendo mente que o Singleton, apesar de ser um padrão (aplica uma linguagem universal), é o mais conhecido dentre os padrões.
Conclusão
Somente utilize o Singleton na circunstância de que a classe que está sendo utilizada precisa somente de uma instância no projeto.
Não se policie, o Singleton garante que somente uma instância será criada.
Tome cuidado para não utilizar Singleton em classes que na lógica de negócio do projeto não têm indicativos de que elas devem ter sempre somente uma única instância, mesmo quando no código atual do projeto elas tenham, cada uma, somente uma instância sendo utilizada.
Ter somente uma instância sendo utilizada devido a quantidade de usuários ou a alguma limitação atual do software não implica em sempre ter de utilizar somente uma instância.
Isso poderá ser um problema para outros programadores quando forem evoluir o software, digo, caso você utilize o Singleton nessas circunstâncias.
Então é isso.
Por fim, não deixe de se inscrever na 📩 lista de e-mails do Blog para receber os conteúdos de desenvolvimento e codificação limpa exclusivos... e em primeira mão.
Abraço.
Fontes
Use a Cabeça! Padrões de Projetos
Comentários Facebook