Singleton
Garante que uma classe tenha apenas uma instância e fornece um ponto de acesso global a ela.
Intenção
Restringir a instanciação de uma classe a um único objeto e fornecer um ponto de acesso conhecido para essa instância, garantindo acesso coordenado a um recurso compartilhado.
Problema
Múltiplas partes da sua aplicação precisam acessar o mesmo recurso compartilhado — como um armazenamento de configuração, um pool de conexões, ou um serviço de logging — mas criar múltiplas instâncias causaria conflitos, estado duplicado ou desperdício de recursos. Não existe um mecanismo nativo da linguagem que garanta uma única instância e ainda permita inicialização preguiçosa (lazy).
Solução
Torne o construtor padrão privado e forneça um método estático de criação que atua como o construtor. Na primeira chamada, ele cria a instância e a armazena em cache; nas chamadas seguintes, retorna a instância em cache. Segurança em concorrência precisa ser considerada em ambientes com múltiplas threads.
Participantes
- Singleton — a classe que gerencia sua própria instância única e fornece um acessor estático
- Client — qualquer código que obtém a instância através do acessor estático, em vez de construção direta
Vantagens
- Garante que apenas uma instância da classe exista durante todo o ciclo de vida da aplicação
- Fornece um ponto de acesso global a essa instância sem depender de variáveis globais
- A instância é criada apenas quando solicitada pela primeira vez, permitindo inicialização preguiçosa
Desvantagens
- Viola o Princípio da Responsabilidade Única ao acoplar o gerenciamento de instância com a lógica de negócio
- Pode mascarar um design ruim ao esconder dependências em vez de torná-las explícitas via injeção no construtor
- Dificulta testes unitários porque o estado global persiste entre os testes
- Exige tratamento especial em ambientes com múltiplas threads para evitar condições de corrida durante a inicialização
Analogia do mundo real
Um país tem exatamente um governo. Independente das identidades pessoais de quem forma o governo, o título 'O Governo de X' é um ponto de acesso global que identifica o grupo no comando. Você não cria um novo governo toda vez que alguém precisa interagir com ele — você acessa o que já existe.
Casos de uso
- Pool de conexões de banco de dados compartilhado por toda a aplicação
- Registro de configurações da aplicação, válido globalmente
- Serviço de logging centralizado
- Acesso a interface de hardware, como uma fila de impressão
- Camada de cache que precisa ser consistente entre módulos
Exemplos de código
import java.time.Instant;
import java.util.UUID;
public final class DatabaseConnection {
private static final class Holder {
private static final DatabaseConnection INSTANCE =
new DatabaseConnection("localhost", 5432);
}
private final String connectionId;
private final String host;
private final int port;
private final Instant connectedAt;
private DatabaseConnection(String host, int port) {
this.host = host;
this.port = port;
this.connectionId = UUID.randomUUID().toString();
this.connectedAt = Instant.now();
}
public static DatabaseConnection getInstance() {
return Holder.INSTANCE;
}
public String query(String sql) {
return "[%s] Executing on %s:%d: %s".formatted(connectionId, host, port, sql);
}
public static void main(String[] args) {
DatabaseConnection db1 = DatabaseConnection.getInstance();
DatabaseConnection db2 = DatabaseConnection.getInstance();
System.out.println(db1 == db2); // true
System.out.println(db1.query("SELECT 1"));
// [uuid] Executing on localhost:5432: SELECT 1
}
}Singleton thread-safe usando o idiom Initialization-on-Demand Holder. A JVM só carrega a classe Holder (e cria a instância) na primeira chamada a getInstance(), o que dá lazy initialization thread-safe sem precisar de synchronized nem de dupla checagem.
Padrões relacionados
Factory Method
Define uma interface para criar um objeto, mas deixa as subclasses decidirem qual classe instanciar. Factory Method permite que uma classe adie a instanciação para suas subclasses.