Singleton: Limitando e Distribuindo
O nosso Jogo tem uma característica muito especial: ele é traduzido dinamicamente, sem a necessidade de recompilação. Para isso, estamos colocando todo o texto em uma classe especial, a TIdioma. O problema é que, se nós estivéssemos utilizando um objeto simples, a cada “new” que déssemos, teríamos uma cópia inútil do objeto ocupando espaço inutilmente. Dessa forma, se tivéssemos 500k de texto, poderíamos estar ocupando vários megas da memória.
Isso, quando temos um projeto pequeno, não faz tanta diferença: acaba-se sabendo tudo que está ou não na memória, principalmente quando somente um programador fez aquilo tudo. Porém, há uma grande falha de programação: quando agregássemos um novo programador, ele teria que saber TUDO que já foi feito, da forma que foi feita, entendendo a lógica de tudo que já tá pronto para, aí sim, programar sem erros.Para resolver esse problema, o primeiro padrão de projeto que usamos para resolver um problema no Jogo foi o Singleton. Esse padrão GoF garante que a classe possui somente uma instância, criando um ponto de acesso global à ela. Isso é feito da seguinte forma: o programador NÃO cria um objeto daquela classe. Ao invés disso, a própria classe testa se ela possui uma instância dela mesma. Em caso negativo, a classe cria e retorna essa instância para o programador; em caso positivo, ela simplesmente retorna essa instância já criada.
Utilizamos esse tal de Singleton para garantir que, durante toda a execução do nosso programa, tivéssemos somente uma ÚNICA classe de tradução jogada na memória. Ela é criada no início do programa e, então, todo acesso à TIdioma é realizado via um ponteiro para ela.
O singleton pode ser aplicado à qualquer classe que você queira limitar. Abaixo vai o código genérico que deve ser aplicado:
package
{
public class TSingleton
{
// Atributo de instância
private static var _instancia:TSingleton = new TSingleton();
Esse atributo _instancia é a chave toda do Singleton e será explicado mais abaixo. O interessante é ver que, com o = new TSingleton(); a instância é inicializada na hora da criação do objeto.
// Atributo de teste private var _valor:int;
_valor é um atributo qualquer. Vamos usar aqui só para testar o funcionamento do padrão Singleton.
O construtor de uma classe Singleton deveria, por definição, ser protegido, para que, ao tentar usá-lo, o compilador desse um erro. Porém, o Action Script 3.0 NÃO PERMITE construtores privados. Como resolver isso?
//-----------------------------------------------------------
public function TSingleton()
{
if (_instancia)
throw new Error("Uso: TSingleton.Instancia.Metodo()");
}
Quando _instancia é iniciada lá em cima, na criação da classe, o construtor é chamado a 1ª e única vez. Todas as outras vezes que alguém tentar construir na mão a classe, o teste é vai garantir que não seja possível criar uma segunda instância.
“Mas Tiago, se eu num posso chamar o construtor, como que eu vou criar um objeto da TSingleton???”. Isso é fácil. Você NÃO CRIA! “Mas hein?!?”
//-----------------------------------------------------------
// Esse método é o que iremos chamar daqui pra frente
// ao invés do construtor da classe
public static function get Instancia():TSingleton
{
return TSingleton._instancia;
}
Toda vez que você precisar acessar um método de uma classe com o Singleton (no nosso caso, a própria TSingleton), você vai usar pela linha TSingleton.Instancia.Metodo() (onde Metodo() é qualquer método da classe), garantindo que em TODAS AS VEZES você está acessando a mesma instância. Abaixo, mais 2 outros métodos de teste.
//-----------------------------------------------------------
// Método simples para retornar um atributo qualquer,
// nesse caso, ‘valor’
public function Valor():int
{
return this._valor;
}
//-----------------------------------------------------------
// Como o método anterior, mas agora setando o valor do
// atributo
public function SetarValor( valor:int )
{
this._valor = valor;
}
//-----------------------------------------------------------
}
}
Agora vai uma classe extra mostrando como usar uma classe com o padrão Singleton. A lógica de uso, nesse caso específico, está imbutida no construtor da classe, mas poderia estar em qualquer local.
Explicando passo a passo:
import TSingleton;
TODA classe que terá visibilidade de Singleton DEVE incluí-la. Dessa forma, mesmo sendo uma classe Global, áreas do programa que NÃO deveriam vê-la, NÃO verão. Isso evita muitos problemas de acoplamento, efeitos colaterais e melhora muito a reutilização.
public function TPrincipal()
{
// Provocando o erro:
//var teste:TSingleton = new TSingleton();
TSingleton.Instancia.SetarValor(345);
this.Exibe();
}
Como o construtor da TSingleton está protegido do acesso do programador, usar var teste:TSingleton = new TSingleton() vai resultar no erro:
Error: Uso: TSingleton.Instancia.Metodo()
at TSingleton$iinit()
at TPrincipal$iinit()
Lembrando que Uso: TSingleton.Instancia.Metodo() é a string que passamos para throw new Error lá no construtor de TSingleton, permitindo que vocês troquem a mensagem para algo mais descritivo, caso queiram.
Só para terminar, eu ressalto que esse é somente um dos vários algoritmos diferentes para o Singleton que vocês podem encontrar aí pela internet. Ele foi criado pelo Douglas prá nossa TIdioma há algumas poucas semanas, pois nenhum dos que encontramos era satisfatório o suficiente. Alguém aí tem mais algum exemplo do Singleton?
PS.: Aqueles que querem mesmo testar o Singleton, os arquivos estão aqui ó. Download Action Script 3.0 Singleton class (portuguese) here. Saiba mais: Wikipedia
Artigos Relacionados:
Sem artigos relacionados.

By Douglas, 28/09/2007 @ 03:53
Fala Tiago!
Lindo o comentário no Cuba Games!
Mais uma vez, ta valendo o esforço, né?