Mão na massa: Singleton

Neste post apresentaremos o tão famoso padrão Singleton!

Problema

Como já vimos antes no padrões Abstract Factory e Factory Method é possível criar um objeto que fique responsável por criar outros objetos. Desta maneira nós centralizamos a criação destes objetos e podemos ter mais controle sobre eles.

Imagine o exemplo da fábrica de carros do padrão Factory Method. A classe fábrica centraliza a criação de objetos carro. Por exemplo, se fosse necessário armazenar quantos carros foram criados, para elaborar um relatório de quais foram os carros mais vendidos, seria bem simples não? Bastaria adicionar um contador para cada tipo de carro e, ao executar o método que cria um carro, incrementar o contador referente a ele.

Vamos então ver como ficaria a nossa classe fábrica, para simplificar, apenas retornamos uma String para dizer que o carro foi criado:

public class FabricaDeCarro {
	protected int totalCarrosFiat;
	protected int totalCarrosFord;
	protected int totalCarrosVolks;

	public String criarCarroVolks() {
		return new String("Carro Volks #" + totalCarrosVolks++ + " criado.");
	}

	public String criarCarroFord() {
		return new String("Carro Ford #" + totalCarrosFord++ + " criado.");
	}

	public String criarCarroFiat() {
		return new String("Carro Fiat #" + totalCarrosFiat++ + " criado.");
	}

	public String gerarRelatorio() {
		return new String("Total de carros Fiat vendidos: " + totalCarrosFiat
				+ "\nTotal de carros Ford vendidos: " + totalCarrosFord
				+ "\nTotal de carros Volks vendidos: " + totalCarrosVolks);
	}

}

A cada carro criado nós incrementamos o contador e exibimos a informação que o carro foi criado. No final adicionamos um método que gera um relatório e mostra todas as vendas.

O código cliente seria algo do tipo então:

	public static void main(String[] args) {
		FabricaDeCarro fabrica = new FabricaDeCarro();
		System.out.println(fabrica.criarCarroFiat());
		System.out.println(fabrica.criarCarroFord());
		System.out.println(fabrica.criarCarroVolks());

		System.out.println(fabrica.gerarRelatorio());
	}

Uma excelente solução não? Agora imagine que, em algum outro lugar do código acontece isso:

		fabrica = new FabricaDeCarro();
		System.out.println(fabrica.gerarRelatorio());

Todos os dados até o momento foram APAGADOS! Todas as informações estão ligadas a uma instância, quando alteramos a instância, perdemos todas as informações. Qual o centro do problema então?

Temos que proteger que o objeto seja instanciado. Como fazer isso?

Singleton

A intenção do padrão é esta:

“Garantir que uma classe tenha somente uma instância e fornece um ponto global de acesso para a mesma.” [1]

Pronto, achamos a solução! Com o uso do padrão garantimos que só teremos uma instância da classe fábrica.

O padrão é extremamente simples. Para utilizá-lo precisamos apenas de:

Uma referência para um objeto fábrica, dentro da própria fábrica:

public class FabricaDeCarro{
	public static FabricaDeCarro instancia;
}

Não deixar o construtor com acesso público:

public class FabricaDeCarro {

	public static FabricaDeCarro instancia;

	protected FabricaDeCarro() {
	}
}

E por fim um método que retorna a referência para a fábrica:

public class FabricaDeCarro {

	public static FabricaDeCarro instancia;

	protected FabricaDeCarro() {
	}

	public static FabricaDeCarro getInstancia() {
		if (instancia == null)
			instancia = new FabricaDeCarro();
		return instancia;
	}
}

Pronto, esta agora é uma classe Singleton. Ao proteger o construtor nós evitamos que esta classe possa ser instanciada em qualquer outro lugar do código que não seja a própria classe.

O código cliente ficaria da seguinte forma:

	public static void main(String[] args) {
		FabricaDeCarro fabrica = FabricaDeCarro.getInstancia();
		System.out.println(fabrica.criarCarroFiat());
		System.out.println(fabrica.criarCarroFord());
		System.out.println(fabrica.criarCarroVolks());
		System.out.println(fabrica.gerarRelatorio());
	}

Perceba que, mesmo que em outra parte do código apareça algo do tipo:

		fabrica = FabricaDeCarro.getInstancia();
		System.out.println(fabrica.gerarRelatorio());

Não será perdido nenhuma informação, pois não houve uma nova instanciação. O método getInstancia() verifica se a referência é válida e, se preciso, instância ela e depois retorna para o código cliente.

Seria possível, no código cliente, nem mesmo utilizar uma referência para uma fábrica, veja o código a seguir:

		System.out.println(FabricaDeCarro.getInstancia().gerarRelatorio());

Um pouco de teoria

Já mostramos em código que a maior vantagem do Singleton é unificar o acesso das instâncias. Além disso msotramos também que não é preciso nem mesmo criar referências para classes Singleton.

No C++ nós temos as tão temidas variáveis globais. Temidas não pela sua natureza, mas pelo uso que se faz de variáveis globais. As classes Singleton são uma excelente saída quando é necessário “globalizar” certos aspectos do programa.

Em concorrência é muito comum utilizar recursos compartilhados que precisam de um acesso unificado, geralmente utilizando monitores. Encapsular o recurso compartilhado em uma classe Singleton torna muito mais fácil sincronizar o acesso.

Outro fator bem positivo do padrão é que, ao mesmo tempo que nós unificamos o acesso aos recursos, nós compartilhamos todos eles! Lembre que não é necessário criar referências, ou seja, qualquer objeto/classe/método em qualquer lugar do seu programa tem acesso aos recursos.

Isto também pode ser visto como uma grande desvantagem, pois não é possível inibir o acesso a classe Singleton. Qualquer parte do código por chamar o método getInstance(), pois ele é estático, e ter acesso aos dados da classe.

Código fonte completo

O código completo pode ser baixado no seguinte repositório Git: https://github.com/MarcosX/Padr-es-de-Projeto.

Os arquivos estão como um projeto do eclipse, então basta colocar no seu Workspace e fazer o import.

Se gostou do post compartilhe com seus amigos e colegas, senão, comente o que pode ser melhorado. Encontrou algum erro no código? Comente também. Possui alguma outra opinião ou alguma informação adicional? Comenta ai! 😀

Referências:

[1] GAMMA, Erich et al. Padrões de Projeto: Soluções reutilizáveis de software orientado a objetos.

Anúncios