Garantir que uma classe tenha somente uma instância e fornecer um ponto global de acesso para a mesma.
É importante para algumas classes ter uma, e apenas uma, instância.
Como garantimos que uma classe tenha somente uma instância e que essa instância seja facilmente acessível? Uma variável global torna um objeto acessível, mas não impede você de instanciar múltiplos objetos.
Uma solução melhor seria tornar a própria classe responsável por manter o controle da sua única instância. A classe pode garantir que nenhuma outra instância seja criada (pela interceptação das solicitações para criação de novos objetos), bem como pode fornecer um meio para acessar sua única instância. Este é o padrão Singleton.
Use o padrão Singleton quando:
for preciso haver apenas uma instância de uma classe, e essa instância tiver que dar acesso aos clientes através de um ponto bem conhecido;
a única instância tiver de ser extensível através de subclasses, possibilitando aos clientes usar uma instância estendida sem alterar o seu código.
Metamodelo original Singleton
Modelo de aplicação Singleton
Singleton: pode ser responsável pela criação da sua própria instância única e define uma operação getInstance que permite clientes acessarem sua única instância.
Clientes acessam uma instância Singleton unicamente pela operação getInstance da Singleton.
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getInstance(){
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
Acesso controlado à instância única. Como a classe Singleton encapsula a sua única instância, possui controle total sobre como e quando os clientes a acessam.
Espaço de nomes reduzido. O padrão Singleton representa uma melhoria em relação ao uso de variáveis globais.
Permite um refinamento de operações e da representação. A classe Singleton pode ter subclasses e é fácil configurar uma aplicação com uma instância dessa classe estendida.
Permite um número variável de instâncias. O padrão torna fácil mudar de ideia, permitindo mais de uma instância da classe Singleton. Além disso, você pode usar a mesma abordagem para controlar o número de instâncias que a aplicação utiliza. Somente a operação que permite acesso à instância de Singleton necessita ser mudada.
O padrão Singleton deve ser utilizado com muito cuidado e nas situações em que realmente fizer sentido ter apenas uma instância de uma classe. O resultado é que o Singleton acaba sendo usado como uma variável global da orientação a objetos, o que pode reduzir a flexibilidade da aplicação deixando sua modelagem deficiente. Por ele ser utilizado mais em situações inadequadas, muitos acabam considerando o Singleton como uma má prática. Sendo assim, toda vez que for utilizar um Singleton reflita bastante se sua utilização é mesmo necessária.
Muitos padrões podem ser implementados usando Singleton, alguns são Abstract Factory, Builder e Prototype.
O padrão de projeto Singleton é um dos padrões mais simples e comuns da programação orientada a objetos.
Este padrão garante que apenas uma única instância de uma classe seja criada durante toda a execução do programa.
Isso é útil em muitos casos, como quando você precisa gerenciar recursos compartilhados, como conexões de banco de dados, logs ou configurações.
É importante notar que a implementação padrão do Singleton não é thread-safe, o que significa que pode haver problemas se várias threads tentarem acessar o método getInstance() ao mesmo tempo. Se a thread-safety é importante para o seu caso de uso, você pode usar outras implementações variantes do padrão Singleton que resolvam esse problema.
Nenhuma técnica especifica é utiliza neste padrão.
Para aplicar o padrão de projeto Singleton em um projeto, você pode seguir os seguintes passos:
Identificar ou criar a classe que será a Singleton;
Declarar um atributo privado e estático da classe Singleton;
Criar um construtor privado para a classe Singleton. Isso garante que outras classes não possam instanciar a classe diretamente;
Criar um método estático getInstance() que retorna a instância Singleton. Dentro desse método, verifique se a instância já foi criada e, se não, crie uma nova instância;
Usar o método getInstance() para obter a instância Singleton em outras partes do seu código.
Se a thread-safety é importante, escolher uma variante do padrão Singleton que resolva o problema.
class Main {
public static void main(String[] args) {
Playlist playlist = Playlist.getInstance();
playlist.adicionarMusica("Bohemian Rhapsody");
playlist.adicionarMusica("Stairway to Heaven");
playlist.mostrarPlaylist();
}
}
import java.util.ArrayList;
public class Playlist {
private static Playlist instance;
private ArrayList<String> musicas;
private Playlist() {
musicas = new ArrayList<>();
}
public static Playlist getInstance() {
if (instance == null) {
instance = new Playlist();
}
return instance;
}
public void adicionarMusica(String musica) {
musicas.add(musica);
}
public void excluirMusica(String musica) {
musicas.remove(musica);
}
public void limparPlaylist() {
musicas.clear();
}
public void mostrarPlaylist() {
for (String musica : musicas) {
System.out.println(musica);
}
}
}
Esta é uma implementação do padrão Singleton que resolve o problema de thread-safety em Java usando o conceito de inicialização estática (eager initialization).
Na inicialização estática, a instância do Singleton é criada no momento do carregamento da classe pela JVM. Isso significa que a criação do objeto é garantida antes que qualquer thread possa acessá-lo. Como o Java garante a execução de blocos static de forma thread-safe, essa abordagem evita problemas de concorrência, sem precisar de synchronized.
Nessa implementação, a instância é criada na inicialização estática da classe usando a palavra-chave final, o que garante que a instância seja única e imutável. Isso elimina a necessidade de sincronização, o que pode melhorar o desempenho em ambientes de threads múltiplas.
O método getInstance() retorna a instância única, que já foi criada na inicialização estática. Como a instância é estática e final, ela é inicializada apenas uma vez e, em seguida, mantida em cache para uso posterior.
Essa implementação é adequada para casos em que a criação da instância é barata em termos de tempo e recursos, e quando a instância pode ser criada sem dependências externas. No entanto, se a criação da instância for cara ou exigir inicialização tardia, a inicialização estática pode não ser a melhor abordagem. Nesse caso, é possível usar outras técnicas de inicialização, como a inicialização preguiçosa (Item 3.2).
ATENÇÃO: ❌ Pode desperdiçar recursos – A instância é criada mesmo que nunca seja usada. Se o objeto for pesado e raramente necessário, essa abordagem pode não ser a ideal.
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
DIFF Singleton tradicional x Singleton com Inicialização Estática - Fonte: Criado pela autora com https://www.diffchecker.com/
Esta é uma implementação do padrão Singleton que resolve o problema de thread-safety em Java usando o conceito de inicialização preguiçosa (lazy initialization), sincronização e Double-Checked Locking.
Nessa implementação, usamos a palavra-chave volatile para garantir que a instância seja sempre atualizada em todas as threads. A palavra-chave volatile evita que a instância seja parcialmente criada por otimizações da JVM.
Também usamos sincronização para garantir que apenas uma thread possa criar a instância da classe.
O método getInstance() agora verifica se a instância é nula e, se for, entra em um bloco sincronizado. Dentro desse bloco, verificamos novamente se a instância é nula e, se for, criamos uma nova instância da classe (estratégia de trava dupla).
Como a sincronização garante que apenas uma thread possa entrar no bloco sincronizado, podemos ter certeza de que apenas uma única instância da classe será criada.
Essa implementação é segura em ambientes multi-threaded, mas tenha em mente que a sincronização pode ter um impacto no desempenho do seu sistema. Estima-se que métodos sincronizados sejam cerca de 100 vezes mais lentos que métodos não sincronizados.
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
DIFF Singleton tradicional x Singleton com Inicialização Preguiçosa - Fonte: Criado pela autora com https://www.diffchecker.com/
Na implementação "Holder Class Idiom" ou "Initialization-on-Demand" ou "Holder Singleton", a classe Singleton é definida com um construtor privado e uma classe interna privada (um "holder"), que é responsável por conter a instância Singleton estática e final.
Essa instância também é inicializada de maneira preguiçosa quando a classe Holder é carregada pelo classloader.
Essa abordagem é segura para threads e é relativamente fácil de entender e implementar.
O carregamento da classe interna só ocorre quando necessário, otimizando o uso de memória.
O Holder Singleton é uma das melhores formas de implementar Singleton em Java porque:
✅ É fácil de escrever e entender.
✅ Não exige sincronização manual, garantindo bom desempenho.
✅ Funciona bem mesmo em ambientes multithreaded.
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
DIFF Singleton tradicional x Holder Singleton - Fonte: Criado pela autora com https://www.diffchecker.com/
Nessa implementação, a enumeração é usada para criar a instância única da classe Singleton. Como as enumerações são tratadas de maneira especial pelo compilador Java, a instância é criada automaticamente e garante-se que seja única.
Para usar a instância da classe Singleton, basta chamar Singleton.INSTANCE. Isso garante que a instância seja única e imutável.
Essa implementação é considerada uma das formas mais seguras e eficientes de criar um Singleton em Java, pois garante a segurança da thread e é à prova de serialização. Além disso, é conciso e fácil de entender.
No entanto, essa abordagem só é adequada se você precisar de uma única instância da classe e se essa instância não precisar de muita personalização. Se você precisar de várias instâncias personalizadas ou precisar fazer mudanças na instância em tempo de execução, outras técnicas Singleton podem ser mais adequadas.
⚠️ Quando NÃO Usar o Enum Singleton?
❌ Se for necessário lazy initialization – A instância é criada logo no carregamento da classe, o que pode ser um problema se ela nunca for utilizada.
❌ Se precisar estender outra classe – Como enums não podem estender outras classes, essa abordagem não permite herança.
public enum Singleton {
INSTANCE;
// métodos da classe Singleton
}
class Main {
public static void main(String[] args) {
Singleton.INSTANCE.metodoX();
}
}
Outra abordagem menos comum é a inicialização por meio de um método de fábrica com controle de acesso (access control factory method) - padrão Factory Method. Nessa abordagem, a classe Singleton tem um método estático que retorna a instância Singleton, e esse método controla o acesso à instância.
Apesar de menos comum do que as outras, esta abordagem pode ser útil em alguns casos em que a criação de instâncias é complexa e o controle de acesso é importante. No entanto, é menos seguro para threads do que outras abordagens, pois exige sincronização em cada chamada ao método getInstance(). Estima-se que métodos sincronizados sejam cerca de 100 vezes mais lentos que métodos não sincronizados.
Vantagens do Singleton por Fábrica:
✔ Encapsulamento – O Factory Method separa a criação do objeto do seu uso.
✔ Flexibilidade – Podemos modificar a forma como a instância é criada sem alterar a classe Singleton.
✔ Testabilidade – Facilita a substituição do Singleton por um Mock para testes unitários.
⚠️ Possíveis Desvantagens:
❌ Pode adicionar complexidade desnecessária – Se não for necessária flexibilidade extra, o padrão Holder Singleton ou Enum Singleton pode ser mais simples e seguro.
public class Singleton {
private static Singleton instance;
private Singleton() {
// construtor privado para evitar que outras classes instanciem essa classe
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
DIFF Singleton tradicional x Singleton com Fábrica de Acesso - Fonte: Criado pela autora com https://www.diffchecker.com/
Comparação entre diferentes implementações do padrão Singleton - Fonte: Criado pela autora com https://www.diffchecker.com/
O padrão Singleton, cuja ideia é garantir que uma classe tenha apenas uma instância em toda a aplicação. No entanto, ter um acesso global a uma variável tende a ser um mau sinal.
O problema é que fornecer um ponto de acesso global para uma única instância é basicamente criar uma instância global. Criar variáveis globais é um grande transtorno pois, dado um momento específico da aplicação, é muito difícil ter certeza do estado dessa variável global (já que ela pode ser alterada em qualquer lugar). O código que usa uma instância global precisa sempre ficar se perguntando o estado dessa instância antes de tomar ações. Isso não quer dizer que usar o Singleton seja ruim. Caso você realmente precise de uma instância global, o padrão propõe uma boa solução. Mas ter e acessar uma instância global não deveriam se tornar um problema no seu código.
O padrão Singleton é frequentemente criticado porque pode introduzir problemas de design e manutenção no código. As principais razões para isso são:
🔥 1. Viola o Princípio da Responsabilidade Única (SRP)
O Singleton mistura duas responsabilidades:
Controlar a criação da instância (responsabilidade de gerenciamento de objetos).
Fornecer a lógica de negócio da classe.
Isso torna o código menos modular e mais difícil de testar.
🔥 2. Dificulta Testes Unitários
Como o Singleton mantém um estado global, ele pode causar efeitos colaterais indesejados nos testes:
Os testes podem se influenciar mutuamente se modificarem o estado do Singleton.
É difícil substituir o Singleton por um mock sem modificar o código original.
🔥 3. Cria Acoplamento Excessivo
Classes que dependem de um Singleton ficam fortemente acopladas a ele. Isso dificulta a substituição por outra implementação no futuro, tornando o código menos flexível.
🔥 4. Pode Mascarar um Código Mal Estruturado
O Singleton é frequentemente usado para compartilhar estado global, mas isso pode ser um sintoma de um design ruim. Em muitos casos, injeção de dependências (DI) ou padrão Factory seria uma alternativa melhor.
🔥 5. Problemas com Concorrência
Se a implementação do Singleton não for thread-safe, podem ocorrer condições de corrida, levando à criação de múltiplas instâncias inesperadamente.
🔎 Resumo: O Singleton pode ser útil em casos específicos, mas seu uso excessivo pode resultar em código difícil de manter, testar e evoluir.
O Singleton tem várias implementações porque diferentes contextos exigem soluções diferentes.
As diferentes implementações de Singleton surgem para equilibrar eficiência, segurança, flexibilidade e testabilidade, dependendo do cenário.
📌 Motivo: Evita múltiplas conexões simultâneas, garantindo que todas as partes do sistema utilizem uma única conexão compartilhada (ou um pool gerenciado).
📌 Motivo: Centraliza configurações carregadas de arquivos ou variáveis de ambiente para evitar leitura repetitiva e desperdício de memória.
📌 Motivo: Garante que todos os logs sejam registrados em um único local, evitando instâncias duplicadas que possam desorganizar os registros.
📌 Motivo: Permite armazenar e recuperar dados frequentemente acessados sem precisar buscar no banco de dados repetidamente.
📌 Motivo: Centraliza as informações de sessão dos usuários para garantir consistência e controle de autenticação.
O Spring Framework é um dos frameworks Java mais populares e oferece o padrão Singleton de forma implícita através de seu container de injeção de dependência (DI). No Spring, os beans (objetos gerenciados pelo Spring) são, por padrão, Singletons, ou seja, uma única instância do bean é criada e compartilhada dentro do contexto da aplicação.
No Django, o Singleton é mais sutil, mas muitos componentes internos, como o Django ORM e o gerenciador de configurações, seguem o conceito de ter uma instância única para toda a aplicação. Por exemplo, a configuração do Django é tratada de forma global, o que impede que diferentes partes do sistema carreguem configurações diferentes.
No ASP.NET Core, o padrão Singleton é amplamente utilizado para o gerenciamento de dependências, especialmente dentro do container de injeção de dependências (DI). Quando você registra um serviço como Singleton no container DI, a instância única é criada e utilizada durante toda a vida útil da aplicação.