1. Singleton

lonely 2378396 1280

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.

singleton
Figure 1. Modelagem de uma classe que implementa o padrão Singleton

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.

singleton config
Figure 2. Modelagem de uma classe que implementa o padrão Singleton para gerenciamento de configurações de um sistema

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