1. Singleton
1.1. Definição
📘"Garante que uma classe tenha apenas uma instância e fornece um ponto global de acesso a ela." [UCPP]
1.2. Aplicabilidade
Este é um padrão aplicado em casos excepcionais.
-
Quando precisa-se garantir que uma classe tenha uma única instância durante toda a execução da app;
-
o objeto a ser criado possui dados únicos, compartilhados por todos os usuários da app;
-
tal objeto exige muitos recursos (como memória) ou tem alto custo de instanciação;
-
representa um dispositivo físico pro qual não faz sentido ter duas instância dentro da mesma app (como uma impresora).
1.3. Modelagem
O padrão Singleton é representado por uma única classe como apresentado na figura a seguir.
O padrão Singleton é aplicado a uma classe por vez. A classe Singleton será aquela que desejamos aplicar o padrão, logo, normalmente é uma classe que já existe e que apenas queremos impedir que sejam criadas múltiplas instâncias dela. Assim, não teremos de fato uma classe chamada Singleton, mas qualquer outro nome relacionado ao sistema que estamos desenvolvendo.
O que faz com que a classe tenha apenas uma única instância, é armazenar tal instância
em um atributo estático e privado dentro da própria classe. Normalmente tal atributo é chamado
simplesmente de instance
. O tipo dele será a própria classe onde ele está sendo declarado.
Se tivermos uma classe Singleton ImpressoraFiscal
, ela terá um atributo ImpressoraFiscal instance
.
O método getInstance()
deve retornar a instância armazenada no atributo instance
,
logo, o tipo do retorno de tal método é a própria classe. No exemplo acima, será ImpressoraFiscal
.
Ele é um método estático, uma vez que não devemos chamá-lo a partir de uma instância, mas sim a partir
da classe para que o método retorne a instância.
Um detalhe importante é que o construtor deve ser privado. Isto é o principal para garantir que
uma única instância da classe será criada.
Para obter-se a instância única, devemos chamar o método getInstance()
, já que não temos
como usar o construtor fora da classe.
Um projeto de exemplo para o diagrama apresentado está disponível aqui. Este é uma exemplo prático que implementa o padrão Singleton para classe que simula a comunicação com uma impressora fiscal.
1.4. Princípios utilizados
Você poderia imaginar que o Singleton adere ao Single-Responsitility Principle (SRP), mas ele não garante que a classe terá uma única responsabilidade. Ele garante que a classe terá uma única instância. Um padrão pode propiciar o SRP, no entanto está mais nas mãos do desenvolvedor garantir um princípio que do padrão.
Pelo fato do padrão ser tão simples que sua modelagem é uma única classe, ele não adere a nenhum princípio específico, mas apenas resolve um problema recorrente em desenvolvimento de software.
1.5. Exemplos
Um sistema pode ter um conjunto de configurações que o usuário pode alterar.
Considere que as configurações do sistema podem ser acessadas e alteradas em diversos locais. Neste caso, o mais apropriado é criar uma classe, chamada por exemplo de Config
, que encapsula o gerenciamento de tais configurações. Se instanciarmos objetos da classe Config
em diferentes locais e alterarmos as configurações, podemos ver os valores atualizados em determinados locais, enquanto aparecem desatualizados em outros. Isto pode trazer problemas para o sistema.
Neste caso, podemos aplicar o padrão Singleton à classe Config
para permitir que uma única instância dela seja criada para todo o sistema.
Mesmo que tenhamos diversas variáveis Config
espalhadas pelo sistema, todas apontarão para um único objeto criado.
Assim, ao alterar uma configuração por meio de qualquer dessas variáveis, estaremos alterando o mesmo objeto.
Logo, teremos acesso aos mesmos dados, independetemente de qual variável utilizarmos.
A figura abaixo apresenta a modelagem para a classe Config
implementado o padrão Singleton.
O projeto config-singleton-spring (zip) fornece a estrutura de um backend com Spring que aplica o padrão Singleton para a classe Config e por padrão para qualquer Spring Bean (objetos cujo ciclo de vida é controlado pelo Spring, como Controllers e Repositories).
1.6. Detalhes de Implementação
Para implementar o padrão Singleton, sempre tenha em mente os seguintes detalhes, conforme pode ser observado na Figura 1 acima.
-
Deve-se explicitamente definir um construtor privado para a classe Singleton. Em java, se nenhum construtor for definido, o compilador automaticamente incluirá um construtor padrão público, que permitirá que a classe seja instanciada de qualquer lugar, quantas vezes desejarmos. Isto vai totalmente contra o padrão Singleton.
-
A única instância a ser criada deve ser definida como um atributo privado e estático. Como ele é estático, só haverá uma única instância armazenada dentro da própria classe. Como é privado, não será possível alterar tal instância depois de o atributo ter sido inicializado.
-
Deve existir um método
getInstance()
que deve verificar se um objeto já foi criado e apenas retorná-lo. Caso contrário, um novo objeto deve ser criado e armazenado no atributo estático comentado no item acima.
2. Padrões Relacionados
Padrões que possuem similaridades ou podem ser usados em conjunto:
3. Onde o padrão é usado no JDK
-
java.awt.Desktop
-
java.lang.Runtime