Instanciando classe em tempo de execução

September 19th, 2009

Todo mundo já passou por uma situação em que a instanciação dos objetos deveria ser dinâmica. Uma das soluções, porém nada prática e profissional, é fazer um switch() ou if-elseif-else.

Isso é comum quando temos um menu que leva para algumas áreas. Poderíamos instanciar esse objeto de uma forma dinâmica. Um loop que instancia um objeto e armazena essas referências num Dictionary ou Array.

São inúmeras situações em que precisamos de instanciação em tempo de execução, e é isso que vamos ver agora.

Objetivo

Precisamos instanciar 5 classes, guardar sua referência num objeto e usá-lo depois.

Exemplo

  1. Crie um AS3 Project chamado GetDefinitionByName (FlashDevelop)
  2. Crie 5 classes: Bola1.as, Bola2.as, Bola3.as, etc

Para instanciar uma classe é muito simples, basta fazermos:

var bola:Bola1 = new Bola1();

Ao colocarmos a keyword new, estamos dizendo a AVM (ActionScript Virtual Machine) que queremos iniciar uma nova instância da classe Bola1. Como a classe Bola1 está no mesmo pacote que a Main, não precisamos especificar seu caminho. Caso estivesse em outro pacote, ou teríamos que importar, ou colocar o nome completo na hora de instanciar, por exemplo:

var bola:meu.pacote.Bola1 = new meu.pacote.Bola1();

Sabendo disso, vamos instanciar a Bola1 dinamicamente:

var bola:Bola1 = getDefinitionByName("Bola1");

Vai explodir um erro.

D:\pessoal\blog\GetDefinitionByName\src\Main.as(25): col: 21 Error: Implicit coercion of a value with static type Object to a possibly unrelated type Bola1.

O tipo de nossa variavel está errado. Isso por que a função getDefinitionByName retorna uma referência de classe. Ou seja, sua classe, ao ser compilada, está na memória da AVM para poder ser instanciada. Como getDefinitionByName retorna uma referência a sua classe, temos que tipá-la com o que ela realmente é: uma classe.

var referenciaBola:Class = getDefinitionByName("Bola1");

Irá explodir outro erro.

D:\pessoal\blog\GetDefinitionByName\src\Main.as(25): col: 31 Error: Implicit coercion of a value with static type Object to a possibly unrelated type Class.

Isso ocorre pois precisamos avisar ao compilador que ele estará nos retornando uma classe através do método getDefinitionByName();

var referenciaBola:Class = getDefinitionByName("Bola1") as Class;

Ok, compilado. Agora temos nossa referência de classe. Podemos instanciar nossa classe Bola1 através da nossa instância de referência de classe referenciaBola.

var referenciaBola:Class = getDefinitionByName("Bola1") as Class;
var bola:Bola1 = new referenciaBola() as Bola1;
addChild(bola);

Pronto, instanciamos a classe Bola1 dinamicamente. Podemos compilar.

Fazendo o loop

Vamos colocar nosso código dentro de um loop, guardar a referência das bolas dentro de um array e fazer qualquer coisa com elas.

for (var i:uint = 1; i <= 5; i++)
{
	var referenciaBola:Class = getDefinitionByName("Bola" + i.toString()) as Class;
	var bola:Bola1 = new referenciaBola() as Bola1;
	bolas.push(bola);
	addChild(bola);
}

Concatemos nossa variável i do loop com a string “Bola”, formando de Bola1 a Bola5. Porém, temos que observar que o tipo da variável bola continua Bola1, o que é errado, pois essa variável assumirá o papel da Bola2, Bola3, etc. Para resolvermos isso, podemos simplesmente trocar de Bola1 para Sprite em ambas situações (do tipo e do cast da função), pois todas nossas bolas têm em comum a superclasse Sprite. Você poderia usar uma interface ou uma classe abstrata nesse caso.

for (var i:uint = 1; i <= 5; i++)
{
	var referenciaBola:Class = getDefinitionByName("Bola" + i.toString()) as Class;
	var bola:Sprite = new referenciaBola() as Sprite;
	bola.x = Math.random() * stage.stageWidth;
	bola.y = Math.random() * stage.stageHeight;
	bolas.push(bola);
	addChild(bola);
}

Compile e observe que a AVM diz que a variável Bola1 não foi encontrada. Como tiramos todas referências estáticas de nossas classes, o compilador não está compilando-as. Com isso, temos que criar 5 variáveis na nossa classe tipando-as, apenas para fins de compilação:

private var b1:Bola1;
private var b2:Bola2;
private var b3:Bola3;
private var b4:Bola4;
private var b5:Bola5;

Podemos compilar agora.

Informações relevantes

Ao criar a instância, podemos passar parâmetros no construtor, dessa forma:

var referenciaBola:Class = getDefinitionByName("Bola" + i.toString()) as Class;
var bola:Sprite = new referenciaBola("parametro1", "parametro2", 2009, new Date()) as Sprite;

E nas classes, tratar esses parâmetros.

No exemplo, todas as classes estavam no mesmo pacote que nossa Main.as compilada. Caso suas classes estejam em outros pacotes, é necessário informar no método getDefinitionByName();

var referenciaBola:Class = getDefinitionByName("meu.pacote.Bola" + i.toString()) as Class;
var bola:Sprite = new referenciaBola() as Sprite;

Isso por que sua classe se chama meu.pacote.Bola e não apenas Bola.

Baixe os arquivos aqui.

Filed under: action script 3 | 1 Comment »

Singleton

July 12th, 2009

Analogia

Você está no mercado fazendo compras. Passa por uma prateleira e coloca os produtos no carrinho. Anda, anda, olha a sua volta, vê outros produtos, coloca-os em seu carrinho e continua fazendo suas compras. Depois de muito “passear”, chega ao caixa, retira os produtos do carrinho, paga e vai embora. Do momento em que entrou no mercado até o momento que saiu, quantos carrinhos você utilizou? Um. Tão somente um.

Conceito

O singleton consiste em retornar à sua aplicação apenas e tão somente UMA INSTÂNCIA DO OBJETO, que será compartilhada e terá ponto de acesso global. Por ser tão simples, talvez seja um pouco confuso de entender no começo. Este é um design pattern criacional.

Diagrama de Classe: Singleton Design Pattern

Diagrama de Classes: Singleton Pattern

Diagrama de Classes: Singleton Pattern

Exemplo

Vamos fazer o Singleton de nosso carrinho de compras.

  1. Crie um novo arquivo Singleton1.fla
  2. Crie uma classe para ser compilada ao nosso .fla. Singleton1.as ou Main.as
  3. Crie uma classe representando o carrinho. Segue o diagrama de classe:
Diagrama de Classe: Carrinho Singleton

Diagrama de Classe: Carrinho Singleton

Veja que existe uma dependência da classe Carrinho com CarrinhoEnforcer. Isso por que não temos construtores privados no ActionScript 3.0, então temos que “contornar”, criando uma classe interna em Carrinho, onde somente esta terá visibilidade, forçando então a dependência (validando nosso Singleton).

Código

Carrinho.as

package
{
 
	/**
	 * ...
	 * @author André Vendramini
	 */
	public class Carrinho
	{
		private var _items:Array;
		private static var instance:Carrinho;
 
		public function Carrinho(enforcer:CarrinhoEnforcer=null)
		{
			if (enforcer == null)
			{
				throw new Error("Apenas uma instância deve ser criada.");
			}
 
			_items = new Array();
		}
 
		public static function getInstance():Carrinho
		{
			if (instance == null)
			{
				//Preste atenção nesta linha:
				instance = new Carrinho(new CarrinhoEnforcer());
 
				/*
				 * Neste momento, estamos instanciando nosso carrinho,
				 * passando como parâmetro apenas uma referência da classe
				 * CarrinhoEnforcer. Como essa classe é interna, apenas
				 * a classe Carrinho tem visibilidade, com isso, esta é a única
				 * maneira de instanciarmos nossa classe Carrinho.
				 */
			}
			return instance;
		}
 
		public function addItem(item:String):void
		{
			_items.push(item);
		}
 
		/* GETTERS AND SETTERS */
		public function get items():Array { return _items; }
	}
 
}
 
/*
 * Classe interna, apenas a classe Carrinho tem visibilidade desta.
 * Com isso, temos a certeza de que apenas a classe Carrinho irá passá-la como parâmetro no construtor.
 * Essa é uma das formas de "burlar" o ActionScript, já que não temos construtores privados.
 */
class CarrinhoEnforcer
{
	public function CarrinhoEnforcer()
	{
 
	}
}

Singleton da classe Carrinho criado. Agora vamos usá-lo. Na classe a ser compilada, entre com o seguinte código:

package
{
	import flash.display.Sprite;
 
	/**
	 * ...
	 * @author André Vendramini
	 */
	public class Singleton1 extends Sprite
	{
		Carrinho.getInstance().addItem("item 1");
		Carrinho.getInstance().addItem("item 2");
		Carrinho.getInstance().addItem("item 3");
		Carrinho.getInstance().addItem("item 4");
		Carrinho.getInstance().addItem("item 5");
 
		trace(Carrinho.getInstance().items);
	}
 
}

O output deverá ser: item 1,item 2,item 3,item 4,item 5. Como nossa função getInstance() é publica e estática, temos ponto de acesso global a instância da classe Carrinho de qualquer escopo. Agora, vamos forçar um erro. Tente instanciar a classe Carrinho normalmente. Ainda na classe a ser compilada:

package
{
	import flash.display.Sprite;
 
	/**
	 * ...
	 * @author André Vendramini
	 */
	public class Singleton1 extends Sprite
	{
		var carrinho:Carrinho = new Carrinho();
	}
 
}

O output:

Error: Apenas uma instância deve ser criada.
at Carrinho()
at Singleton1()

Perceba que a única forma da instância da classe Carrinho ser criada é passando um parâmetro: uma instância da classe CarrinhoEnforcer. Vamos tentar passar essa instância como parâmetro e burlar nosso Singleton? Simples, não?

package
{
	import flash.display.Sprite;
 
	/**
	 * ...
	 * @author André Vendramini
	 */
	public class Singleton1 extends Sprite
	{
		var carrinho:Carrinho = new Carrinho(new CarrinhoEnforcer());
	}
 
}

Output: 1180: Call to a possibly undefined method CarrinhoEnforcer. A classe foi definida DENTRO da própria classe Carrinho.as, tendo apenas esta a visibilidade da mesma. Voilà, nosso Singleton está apto a ser posto em serviço.

Baixe os arquivos aqui.

Aplicabilidade

Este é um pattern extremamente útil se aplicado de forma correta. Por exemplo, em jogos. Para contar pontuação de um jogo, não importa em qual fase esteja, quantos pontos ganhe nem quantas vezes tente. O Singleton se encarrega de cuidar direitinho de tudo isso. Com a certeza de existir apenas 1 ponto de acesso a instância.

Cuidados

Este é um pattern que pode ludibriar no início por sua simplicidade e poder. Não o use abusivamente para corrigir problemas nem contornar situações de orientação a objetos. Por trabalhar com uma função estática e de acesso global, você pode ficar “preguiçoso” de fazer da forma correta e começar a usá-lo de forma errada, apenas para poupar tempo. Os princípios da orientação a objetos não devem ser substituídos, jamais.

Ele quebra uma das leis da orientação a objetos, que diz para cada objeto ter uma responsabilidade. O Singleton possui 2: da acesso global a variável e têm a certeza de única instância.

Farei um post sobre o assunto mais pra frente. Leiam esses materiais:

Curiosidades

Singleton é uma alusão a jogo de cartas. Nos EUA, quando o jogador fica apenas com uma carta na mão, esta é chamada “singleton”.

Este é um padrão GoF.

Filed under: criacionais, design patterns | 1 Comment »

Design Patterns

July 9th, 2009

Observando o que existe em nossa volta captamos algumas situações, estados e comportamentos que, se pararmos para analisar de uma forma exata e calculista, é exatamente o que precisamos.

Observe a estrutura de uma árvore: tronco, galhos, folhas. Uns “conectados” com os outros. O comportamento: das folhas, como uma reage só ao fato de encostar numa outra que está ao lado; dos galhos, você balança um e todas as folhas sofrem a reação.

Agora pense numa fábrica. Você, como cliente, espera o produto pronto da fábrica. Não quer saber como é feito e por quais processos passa. Apenas o quer perfeito, na hora que precisar, pronto para ser usado.

O que isso tem a ver com design patterns? Aí está toda arte. O “segredo” por detrás de conceitos, códigos, padrões, nomenclatura, comportamento, eventos. Não são tão puro e simplesmente “padrões a serem seguidos”. Vamos ter um olhar mais artístico, pois código é arte.

Design Patterns (padrões de projetos), de uma forma bem resumida, são padrões, pensamentos e conceitos que são aplicados a determinadas situações. Uma solução simples, robusta, inteligente e eficaz.
Ao estudá-los e começar a ter essa visão de abstrair e comparar com situações do mundo real, sejam criadas pelo homem ou sejam naturais, começamos a perceber o poder que temos em nossas mãos como programadores. A beleza dos design patterns deve-se ao fato de muitas vezes serem apenas representações em código de algo já existente. Poderosa arma, não?

Com isso fica de fácil compreensão seu código. Fica algo enxuto, bonito, leve, maduro, podendo ser admirado por outro programador durante muito tempo. Quando um artista faz um quadro, com certeza outros irão vê-lo e julgá-lo.

Esta foi uma pequena introdução à área de design patterns do blog. Breve, irei escrever sobre os principais padrões, sempre com uma analogia de facil entendimento, exemplos na prática e como aplicar em projetos.

Referências:

“A arte de transformar situações, estados e comportamento em código”.

Filed under: design patterns | 6 Comments »