Singleton no Java

O padrão de projeto Singleton é um padrão que tem como objetivo garantir que apenas uma única instância da classe alvo é criada. É um dos padrões de projetos mais utilizados. Outro fator interessante do uso do Singleton é quando precisamos ter um único ponto de acesso global para a instância de classe. Por exemplo, alguma classe que possui dados de configuração que são únicos para toda a aplicação.

Existem algumas maneiras de implementar o Singleton. A maneira mais comum é a seguinte:

public class SingletonUm {

	private static final SingletonUm instance = new SingletonUm();

	private SingletonUm() {
	}

	public static SingletonUm getInstance() {
		return instance;
	}
}

O construtor privado (linha 3) impossibilita a criação de novas instâncias, pois, por ser privado, só pode ser referenciado dentro da mesma classe. O método getInstance() (linha 5) fornece o acesso à instância criada na linha 2. Ele é estático para poder ser acessado sem ter a instância (já que o construtor é privado e queremos que a instância seja única). Então para obtermos acesso à classe é só chamar: SingletonUm.getInstance();

O problema dessa abordagem é que se não formos utilizar a instância ou se formos utilizar ela posteriormente, a aplicação terá um objeto desnecessário em memória. Então para termos uma instanciação tardia, devemos adotar outra abordagem. Outra implementação bastante comum é a seguinte:

public class SingletonDois {
	private static SingletonDois instance;

	private SingletonDois() {
	}

	public static SingletonDois getInstance() {
		if (instance == null) {
			instance = new SingletonDois();
		}
		return instance;
	}
}

Com essa verificação  a instância só será criada após chamado o método getInstance(). Para torná-lo thread-safe, é só colocar o modificador synchronized no método. O problema dessa inclusão é o gargalo que teremos ao transformar o método em synchronized, além de outros possíveis problemas. Nesse link tem maiores descrições desses problemas. Para resolver, o Bill Pugh (um dos autores do artigo citado), desenvolveu a seguinte solução:

public class SingletonBillPugh {

	private SingletonBillPugh() {
	}

	private static class SingletonHolder {
		public static final SingletonBillPugh instance = new SingletonBillPugh();
	}

	public static SingletonBillPugh getInstance() {
		return SingletonHolder.instance;
	}
}

Baseado no modelo de inicialização do Java, o código da linha 05 só é chamado após a invocação do método getInstance() (linha 07). Dessa maneira garantimos instanciação tardia e o código ser thread-safe.

Outra maneira mais elegante de se fazer Singleton é o citado no livro Effective Java:

public enum SingletonEnum {
	INSTANCE;
	// Operações do Enum aqui!
}

O enum não pode ser instanciado, só tem uma instância única, que é o nosso Singleton. O único problema dessa abordagem é que temos alguns limitantes no enum, como a impossibilidade de se fazer herança e não temos instanciação tardia.

As dúvidas agora permanecem quando utilizar uma solução ou outra. Minha recomendação é a seguinte. Se não é necessário ter inicialização tardia, utilizar a solução SingletonEnum, desde que a classe alvo não tenha problemas com os limitantes do enum, caso contrário, utilizar a solução SingletonUm. Se é necessário ter instanciação tardia utilizar a solução SingletonBillPugh, desde que não seja necessário realizar tratamento de erros ao chamar o getInstance(), caso contrário, utizar a solução SingletonDois.

Sobre: Thiago Galbiatti Vespa

Thiago Galbiatti Vespa é mestre em Ciências da Computação e Matemática Computacional pela USP e bacharel em Ciências da Computação pela UNESP. Coordenador de projetos do JavaNoroeste, membro do JCP (Java Community Process), consultor Oracle, arquiteto de software de empresas de médio e grande porte, palestrante de vários eventos e colaborador de projetos open source. Possui as certificações: Oracle Certified Master, Java EE 5 Enterprise Architect – Step 1, 2 and 3; Oracle WebCenter Portal 11g Certified Implementation Specialist; Oracle Service Oriented Architecture Infrastructure Implementation Certified Expert; Oracle Certified Professional, Java EE 5 Web Services Developer; Oracle Certified Expert, NetBeans Integrated Development Environment 6.1 Programmer; Oracle Certified Professional, Java Programmer; Oracle Certified Associate, Java SE 5/SE 6