Criar e usar classes singleton e classes imutáveis

Singleton

Um singleton é uma classe que só permite a criação de uma única instância. Isso pode ser útil quando se deseja controlar o acesso a um recurso compartilhado, como uma conexão a um banco de dados.

Implementação de um Singleton

Aqui está um exemplo de como criar uma classe singleton em Java:

public class Singleton {
    // Instância privada estática da própria classe
    private static Singleton instanciaUnica;

    // Construtor privado para evitar instanciamento externo
    private Singleton() {
    }

    // Método público estático para fornecer a única instância
    public static Singleton getInstancia() {
        if (instanciaUnica == null) {
            instanciaUnica = new Singleton();
        }
        return instanciaUnica;
    }

    public void mostrarMensagem() {
        System.out.println("Instância única do Singleton!");
    }
}

Uso do Singleton

public class Main {
    public static void main(String[] args) {
        Singleton instancia = Singleton.getInstancia();
        instancia.mostrarMensagem();
    }
}

Classe Imutável

Uma classe imutável é aquela cujo estado não pode ser alterado após a criação do objeto. As classes imutáveis são intrinsicamente seguras para uso em cenários de concorrência.

Regras para criar uma classe imutável

  1. Torne a classe final para evitar a criação de subclasses.
  2. Torne todos os campos private e final.
  3. Não forneça métodos setter.
  4. Inicialize todos os campos via construtor.
  5. Se algum campo contiver objetos mutáveis, forneça apenas métodos getter que retornem uma cópia do campo ou uma versão imutável dele.

Implementação de uma Classe Imutável

public final class PessoaImutavel {
    private final String nome;
    private final int idade;

    public PessoaImutavel(String nome, int idade) {
        this.nome = nome;
        this.idade = idade;
    }

    public String getNome() {
        return nome;
    }

    public int getIdade() {
        return idade;
    }

    @Override
    public String toString() {
        return "PessoaImutavel{" +
                "nome='" + nome + '\'' +
                ", idade=" + idade +
                '}';
    }
}

Uso da Classe Imutável

public class Main {
    public static void main(String[] args) {
        PessoaImutavel pessoa = new PessoaImutavel("Alice", 30);
        System.out.println(pessoa);

        // Tentativa de modificar a pessoa (não possível devido à imutabilidade)
        // pessoa.setNome("Bob");  // Este método não existe
    }
}

Resumo

  • Singleton: Garante uma única instância da classe com métodos de acesso controlado. Tenha cuidado com a segurança de thread.
  • Imutável: Garante que o estado do objeto não pode ser alterado após sua criação, sendo seguro para uso em ambientes concorrentes.

Perguntas sobre Singleton

  1. Qual é o propósito do padrão de design Singleton?
    • O padrão Singleton é usado para garantir que uma classe tenha apenas uma instância e fornece um ponto global de acesso a essa instância.
  2. Como você pode tornar um Singleton thread-safe em uma aplicação multithread?
    • Você pode tornar um Singleton thread-safe usando a palavra-chave synchronized no método que retorna a instância ou usando a inicialização estática para criar a instância.
  3. Qual é a diferença entre a inicialização preguiçosa e a inicialização ávida em um Singleton?
    • Inicialização preguiçosa cria a instância do Singleton quando ela é necessária pela primeira vez, enquanto a inicialização ávida cria a instância assim que a classe é carregada.
  4. Por que é importante ter um construtor privado em uma classe Singleton?
    • O construtor privado impede que outras classes instanciem diretamente a classe Singleton, garantindo que a única instância seja controlada pela própria classe.

Perguntas sobre Classes Imutáveis

  1. Quais são as principais características de uma classe imutável?
    • Uma classe imutável é final, todos os seus campos são privados e finais, não fornece métodos setter, e qualquer campo que contenha objetos mutáveis retorna cópias desses objetos.
  2. Qual é a importância de fazer cópias defensivas de objetos mutáveis em classes imutáveis?
    Fazer cópias defensivas de objetos mutáveis ao armazená-los ou retorná-los evita que o estado interno da classe imutável seja alterado externamente, mantendo sua imutabilidade.
  3. Como a palavra-chave final ajuda a criar classes imutáveis?
    A palavra-chave final em uma classe impede que a classe seja estendida, e em um campo garante que o campo não pode ser reatribuído após a inicialização.
  4. Como você implementaria uma classe imutável que contém uma lista de elementos?
    Você criaria uma cópia defensiva da lista ao inicializar a classe e ao retornar a lista, para garantir que a lista interna não possa ser modificada externamente.
import java.util.Collections;
import java.util.List;

public final class ListaImutavel {
    private final List<String> elementos;

    public ListaImutavel(List<String> elementos) {
        this.elementos = Collections.unmodifiableList(new ArrayList<>(elementos));
    }

    public List<String> getElementos() {
        return elementos;
    }
}