Pular para o conteúdo
Criacionais

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

DatabaseConnection.java
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.