quinta-feira, 29 de abril de 2010

O padrão de projetos "Singleton"

Bom, com esse post, vou começar uma série de posts sobre padrões de projetos de software (do inglês, Design Patterns) - envolvendo padrões utilizados com o action script 3, de uma forma geral, ou, especificamente, com o framework Flex. Dessa forma, nada melhor que começar por um dos padrões mais utilizados e que possui uma implementação bem simples: o "Singleton".

- Descrição geral: O Singleton é um padrão que garante a existência de apenas uma instância de uma determinada classe em todo o sistema.

- Motivação: Muitos projetos necessitam que algumas classes tenham apenas uma instância no sistema, mantendo um ponto global de acesso a esse objeto. Por exemplo, uma aplicação que precisa de uma infraestrutura para log de dados, poderia ser modelada de forma que exista apenas um objeto responsável em fazer os logs em toda a aplicação (garantindo uma melhor consistência do log gerado). Outro caso em que o Singleton é bastante utilizado é nos modelos de dados das aplicações baseadas em MVC (garantindo a integridade e consistência dos dados em toda a aplicação).

- Implementação:

a) Em JAVA:

Na linguagem JAVA, como existem construtores privados, pode-se implementar um Singleton facilmente.

A forma mais simples e tradicional de implementação é a seguinte:
public class Singleton {
	private static final Singleton INSTANCE = new Singleton();

	//O construtor privado previne a instanciação a partir de outras classes
	private Singleton() {}
 
	public static Singleton getInstance() {
		return INSTANCE;
	}
}
 

A única desvantagem dessa abordagem é que a instância Singleton vai ser criada logo que a classe for inicializada, pois a construção da mesma está em um atributo estático, e isso nem sempre é desejável nas aplicações - pode-se querer retardar esse processo e controlar exatamente o ponto em que instanciação ocorrerá (fazendo uma espécie de "lazing loading", ou inicialização sob demanda).

Dessa forma, foi proposta outra abordagem que contorna esse problema: a chamada solução de Bill Pugh. Nessa abordagem, a classe aninhada não é referenciada em nenhum momento antes do método "getInstance", não sendo, portanto, carregada pelo class loader antes da invocação desse método.

public class Singleton {
	//O construtor privado previne a instanciação a partir de outras classes
	private Singleton() {}
 
	/**
	* O SingletonHolder é carregado na primeira execução do Singleton.getInstance() 
	* ou no primeiro acesso ao SingletonHolder.INSTANCE, nunca antes.
	*/
	private static class SingletonHolder { 
		private static final Singleton INSTANCE = new Singleton();
	}
 
	public static Singleton getInstance() {
		return SingletonHolder.INSTANCE;
	}
 }

Obs.: As duas abordagens apresentadas são "thread-safe", não requerendo nenhum constructo especial da linguagem (por exemplo, "volatile" ou "synchronized").


b) Em Action Script 3:

Em action script, como não existe construtor privado, algumas adequações têm que ser feitas. Assim, uma forma utilizada é a utilização de uma inner class (chamada no exemplo de "SingletonEnforcer") como argumento do construtor da classe Singleton - dessa forma, mesmo esse construtor sendo público, não há possibilidade de instanciar um objeto da classe principal (por não ser possível instanciar um objeto SingletonEnforcer fora dessa classe, para passá-lo como parâmetro).

Assim, a única forma de acessar uma instância da classe principal - a única instância - é através de métodos estáticos do tipo "getInstance()". Na primeira execução desse método, em qualquer ponto do programa, como ainda não foi feita nenhuma instanciação da classe ("instance == null"), vai ser feito a criação de um objeto que vai ser atribuído na variável estática "instance". Nas próximas chamadas ao método getInstance, essa variável já estará atribuída, e o objeto que está armazenado nela será reutilizado ("return instance").

package{ 
	public class ExemploSingleton { 
		private static var instance:ExemploSingleton; 

		public function ExemploSingleton(enforcer:SingletonEnforcer): void { 
			if (enforcer == null) throw new Error("Só pode haver uma instância de ExemploSingleton"); 
		} 

		public static function getInstance() : ExemploSingleton { 
			if (instance == null) 
					instance = new ExemploSingleton( new SingletonEnforcer );
			return instance; 
		} 

	} 

}

//Para bloquear o acesso ao constructor. 
class SingletonEnforcer { }


Obs.: O "if (enforcer == null) throw new Error("Só pode haver uma instância de ExemploSingleton")", dentro do construtor, é só para garantir que não será passado um "null" como parâmetro do mesmo, o que burlaria todo o artifício proposto.


Referências:
http://en.wikipedia.org/wiki/Singleton_pattern
http://pt.wikipedia.org/wiki/Singleton
http://sourcemaking.com/design_patterns/singleton

Nenhum comentário: