Padrão de Projeto: Singleton

Receba em primeira mão o conteúdo exclusivo do Blog, além de promoções de livros e cursos de programação. Você receberá um email de confirmação. Somente depois de confirmar é que poderei lhe enviar o conteúdo exclusivo por email.

Email inválido.
Blog /Android /Padrão de Projeto: Singleton

Padrão de Projeto: Singleton

Vinícius Thiengo30/08/2016
(846) (2) (9) (10)
Go-ahead
"Você não começa com tudo a construir uma parede. Você não diz 'eu estou indo para construir a maior parede que já foi construída.' Você não começa por aí. Você diz: 'Eu vou colocar este tijolo tão perfeitamente como um tijolo pode ser colocado.' Você faz isso todos os dias. E logo você tem uma muralha."
Will Smith
Código limpo
Capa do livro Refatorando Para Programas Limpos
TítuloRefatorando Para Programas Limpos
CategoriaEngenharia de Software
AutorVinícius Thiengo
Edição
Ano2017
Capítulos46
Páginas598
Comprar Livro
Conteúdo Exclusivo
Receba em primeira mão o conteúdo exclusivo do Blog, além de promoções de livros e cursos de programação.
Email inválido

Opa, blz?

Nesse 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: Limitar Instanciação Com Singleton e Internalizar Singleton.

Tópicos presentes no 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 post 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. Nesse 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.

Fontes

Use a Cabeça! Padrões de Projetos.

The volatile keyword in Java.

Vlw.

Receba em primeira mão o conteúdo exclusivo do Blog, além de promoções de livros e cursos de programação.
Email inválido

Relacionado

Padrão de Projeto: Objeto NuloPadrão de Projeto: Objeto NuloAndroid
Use a Cabeça! Padrões de ProjetosUse a Cabeça! Padrões de ProjetosLivros
Padrão de Projeto: State (Estado)Padrão de Projeto: State (Estado)Android
Padrão de Projeto: Decorator (Decorador)Padrão de Projeto: Decorator (Decorador)Android

Compartilhar

Comentários Facebook (2)

Comentários Blog

Para código / script, coloque entre [code] e [/code] para receber marcação especifica.
Forneça seu nome válido.
Forneça seu email válido.
Forneça o comentário.
Enviando, aguarde...