ActionScript


29
Mar 09

Adeus duplicateMovieClip, mas cadê o clone?

No ActionScript 3 não existe o duplicateMovieClip e a DisplayObject não implementa nenhum método de clone (espero que isso não dure muito). Ué, mas qual é o problema? É simples. As vezes não é necessária apenas uma nova instância de um objeto, mas uma que carregue consigo o estado do objeto original. Para isso serve (ou serviria) o método clone. Na prática, em um dado momento, elas são iguais e são diferentes. Isso quer dizer que elas possuem referência a endereços de memória distintos e se uma delas sofrer alteração  a outra se manterá intacta. Ou seja, no momento em que a instância clone é criada ambas possuem o mesmo estado, mas não a mesma identidade.

Onde isso faz falta?

Suponha que você queira manter 2 imagens iguais aparecendo na tela para simular um efeito de espelho ou que você precise mostrar um thumb e uma imagem grande e esta imagem é carregada dinâmicamente. Você não pode simplesmente dar addChild uma segunda vez, pois o seu objeto vai se mover. Sim, ele vai sumir do lugar antigo e aparecer no novo lugar, mas por quê? Não era para ser uma referência na memória? Sim, continua sendo, mas… Todo DisplayObject possui uma propriedade parent que faz referência ao DisplayObjectContainer onde ele foi adicionado. Sempre que o método addChild é chamado o valor da propriedade parent é alterado e consequentemente o DisplayObject deixa de estar no lugar anterior, porque afinal de contas ele só tem um pai.

Para Bitmap felizmente existe um jeitinho, mas para outros DisplayObject, não. Mas o que um Bitmap tem de tão especial? Um objeto Bitmap tem em sua composição um objeto BitmapData, e este por sua vez tem o método clone implementado. É na propriedade bitmapData que estão armazenadas todas as características da imagem. Segue um exemplo com um Bitmap gerado pelo código, mais especificamente um quadrado vermelho, veja como é simples:

// criando um bitmap (quadrado)
var bitmap:Bitmap = new Bitmap(new BitmapData(100, 100, false, 0xBB0000));
 
// adicionando bitmap no palco
this.addChild(bitmap);
 
// criando um novo bitmap com o clone do bitmap data
var bitmapClone:Bitmap = new Bitmap(bitmap.bitmapData.clone());
 
// modificando o posicionamento p/ enxergarmos os 2 elementos
bitmapClone.x = 100;
bitmapClone.y = 100;
 
// adicionando o clone no palco
this.addChild(bitmapClone);

Se uma imagem é carregada dinamicamente o processo é exatamente o mesmo, já que todo objeto Bitmap possui em sua composição um BitmapData sendo ele carregado externamente ou não. Então aproveitamos o que a imagem já tem. Segue um exemplo:

// declara e instancia um objeto loader
var loader:Loader = new Loader();
 
// adiciona listener para o carregmento da imagem
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleLoadComplete);
 
// define uma url para carregamento
var urlRequest:URLRequest = new URLRequest("http://www.nyan.com.br/ico/it.png");
 
// inicia o processo de carregamento
loader.load(urlRequest);
 
// handle para o evento de complete
function handleLoadComplete(event:Event):void 
{
	// cria referencia para a imagem carregada
	var image:Bitmap = loader.content as Bitmap;	
 
	// adiciona a imagem no palco
 	this.addChild(image);
 
	// cria um novo bitmap e adiciona um clone 
	// do BitmapData contido no Bitmap carregado
	var imageCopy:Bitmap = new Bitmap(image.bitmapData.clone());
 
	// adicionando copia no palco
	this.addChild(imageCopy);
	// mudando posicionamento para enxergarmos
	imageCopy.x = 100;
 
}

Tem mais uma coisa interessante nisso: se você declarar um novo Bitmap, atribuindo a ele um BitmapData existente mas sem chamar o método clone, também vai funcionar. A diferença é que se você fizer qualquer alteração no bitmapData através de qualquer um dos Bitmaps, ambos serão alterados, o que não acontece quando invocamos o método clone, pois ele sempre retorna uma nova instância. Este é um bom exemplo pra entender a diferença entre instâncias e referências.


28
Mar 09

Forçando o download de arquivos no Flash

Quantas vezes já não passamos pela situação onde nosso arquivo era uma imagem, um mp3, um pdf, etc e não queríamos que ele abrisse no navegador, mas ao invés disso desejavamos forçar o download?

Não é necessário comprimir o arquivo (gerar um zip, um rar ou qualquer outra coisa) ou forçar pelo php, .net, etc. Você pode usar a  classe FileReference. Todo mundo lembra dessa classe na hora de fazer upload, mas a maioria esquece o método download que abre a caixa de diálogo no navegador para salvar o arquivo. 

O código é muito simples:

// definindo url do arquivo
var fileURL:URLRequest = new URLRequest("http://www.nyan.com.br/ico/feed.png");
 
// instancionado objeto file reference
var fileReference:FileReference = new FileReference();
 
fileReference.download(fileURL);

Só não esqueça de uma coisa: O Flash Player 10 sofreu várias atualizações de segurança e uma delas está diretamente relacionada à classe FileReference. Não é possivel chamar os métodos download ou browser se não for através de uma interação do usuário (pressionando uma tecla ou clicando com o mouse, por exemplo).


25
Mar 09

Template Method

O Template Method é um padrão de projeto que tem como objetivo definir uma base do código (algorítimo ou funcionalidade), deixando que as subclasses completem as tarefas, mas sem alterar a estrutura base.

Se você reparar bem, quando falamos de aproveitamento de código em OOP, esse padrão é a base de quase tudo – ou a base de quase tudo se parece muito com ele. O problema dele em relação ao ActionScript é que ele é baseado em classes e métodos abstratos e como a linguagem ainda não disponibiliza este recurso, podemos apenas tapar um olho e simular o padrão.

Nota: Eu particularmente gosto de usar o prefixo Base em todas as classes que crio em AS e que em teoria deveriam ser classes abstratas. Ao menos assim existe uma forma simples e rápida de identificá-las dentro da estrutura de um projeto ou framework.

Segue um exemplo extremamente simples (e sem funcionalidade) só para entendermos alguns conceitos. 

Classe “abstrata”:

package
{
	public class BaseTest extends MovieClip
	{
		public function BaseTest()
		{
		}
 
		// definindo um método que não pode ser sobrescrito
		public final function finalMethod():void
		{
			// chamada a método opcional (gancho)
                        this.hookMethod();
 
                        // implementação padrão
			this.x = 200;
 
			// chamada ao método que será sobrescrito
			this.customMethod();
		}
 
		// método que deveria ser abstrato
		protected function customMethod():void
		{
			// implementação na classe concreta
		}
 
		// método gancho que pode ou não ser sobrescrito
		protected override function hookMethod():void
		{
		}
        }
}

Classe concreta:

package
{
	public class CustomTest extends BaseTest
	{
		public function CustomTest()
		{
		}
 
		// método que deveria ser abstrato
		protected override function customMethod():void
		{
			// implementação personalizada obrigatória
			this.y = 45;
                }
        }
}

Neste padrão as chamadas normalmente ocorrem mem modo inverso daqueles que normalmente aplicamos. Ao invés de fazer a chamada ao código comum (ou código reutilizado) na classe filha (CustomTest), fazemos as chamadas pela classe pai (BaseTest). Assim, é possivel garantir o fluxo de execução das tarefas pelo objeto.

Podemos observar também que na classe “abstrata” os métodos públicos (que são implementados) são declarados com a palavra reservada final, isto significa (garante) que ele não poderá ser sobrescrito. Os métodos personalizáveis, ao contrário, são declarados como protected permitindo que sejam sobrescritos através de herança.

As principais chamadas que a classe “abstrata” faz são as seguintes:

Métodos concretos

Da classe abstrata – implementações úteis a todas as classes filhas -, da classe concreta ou de outras classes.

Métodos abstratos

Devem obrigatoriamente ser sobrescritos. Isto é um problema pois não conseguimos garantir a sobrescrita de um método utilizando ActionScript a não ser que seja por interfaces.

Métodos ganchos

Podem ou não ser sobrescritos e são uma implementação opcional. É uma maneira de utilizar herança de forma controlada. Estes métodos não possuem implementação na classe abstrata.

 

É interessante que ao usar essa idéia em ActionScript seja definido um padrão para os nomes dos métodos, que facilite a identificação do métodos que são obrigatórios (teoricamente abstratos) e dos que são opcionais, ainda que não se tenha a garantia de um método abastrato.


20
Mar 09

Objetos: instâncias e referências

Instâncias e referências são coisas completamenete distintas. É importante entender que quando você declarar uma variável não está sendo criada nenhuma instância. 

// declarando variável
var content:MovieClip;

variavel

Quando um objeto for criado – uma nova instância pode ser gerada através do operador new -, obtém-se uma referência, que é armazenada na variável.

// cria instância de MovieClip
content = new MovieClip();

instancia

Neste exemplo, content é uma variável que armazena um endereço de memória, no qual existe um objeto do tipo MovieClip. A variável content mantém apenas a referência para o objeto e não o objeto em si. Assim, numa atribuição como a mostrada abaixo, não será criado outro objeto MovieClip, mas uma nova referência para ele.

// atribuindo referencia de content a content2
var content2:MovieClip = content;

referencia


14
Mar 09

Convenções de código

Não que seja uma coisa do ActionScript, mas me surgiu enquanto programando AS3 na AG2.

Um dia o Tiago Schenkel, programador ActionScript da AG2 Pelotas, me mandou um link de convenções de código e melhores práticas para Flex. Uma documentção da própria Adobe - veja aqui -  e disse:

Dani, essa tu vai gostar!

É, realmente gostei. Sempre vi uma penca de padrões quando vivia no universo sistemas, mas Flash na AG2 está no universo interfaces. Fiquei com aquele documento em mente e comecei a me lembrar o quanto a  falava que não conseguia entender a organização do código do Fernando e vice-versa. Até que um dia o Mozart me procurou pra falar que precisávamos  adotar alguma convenção de código. E de fato ele tinha razão. Eu tinha/tenho meus vícios de código, assim como cada um. Agora vivemos num universo de constante adaptação, mas onde todos devem seguir juntos. 

Essa é só mais uma história do mundo real onde a ausência de padronização pode gerar classes confusas e atrasar o trabalho como um todo. Este post é só um pensamento sobre isso. As vezes dá pra se questionar se a tudo não fica burocrático demais. Acho que não! A diferença é que toda uma equipe mantém os mesmos  ”vícios” e assim um pode continuar o trabalho do outro sem ficar varrendo uma classe para saber onde foram colocados os getters. 

Como é o tipo de documento a ser mantido e atualizado por um grupo de pessoas tende a ficar cada vez melhor. Os hábitos vão sendo adotados gradualmente e com o tempo todos escrevem classes muito mas legíveis. Consequentemente, sistemas muito mais claros.


13
Mar 09

Encapsulamento

Encapsulamento é uma das bases da Orientação a Objetos onde a idéia principal é tornar cada objeto auto-suficiente (na medida do possível). Com isso podemos gerar código com um grande índice de reuso, além de facilitar a manutenção.

Vamos tomar como exemplo um ComboBox:

Você adiciona o componente no Stage e insere os dados no mesmo (label, data, etc), certo? O componente é responsável por exibí-los. Assim, podemos dizer que você usa um ComboBox e tem acesso às propriedades necessárias para personalizá-lo ao seu modo (Talvez o combo do flash não seja o melhor exemplo de personalização, mas essa é outra história). Já pensou se você precisasse programar um código para que depois de um numero x de elementos fosse inserida uma rolagem no combo? Felizmente, neste conceito (e no do Flash, graças a Deus!), você não precisa. O combo é responsável pelos seus estados (lembre dessa palavra! Vamos lá, repita comigo: estado, estado, estado…), e é ele quem deve cuidar da sua rolagem independente do número de itens que possua.

Vamos supor que o nosso combo possua uma variável estática (que consequentemente tem o mesmo valor para todos os combos – ou em se tratando de OOP propriamente dita, para todas as instâncias do objeto) chamada MIN_ELEMENTS, que é responsável por armazenar o número mínimo de elementos visíveis, cuja declaração seria a seguinte:

public static var MIM_ELEMENTS:Number = 1;

Então, por descuido ou qualquer outro motivo alguém resolve escrever o código:

ComboBox.MIM_ELEMENTS = -10;

A variável é publica e pode ser acessada de qualquer lugar. Mas me diga, é possível renderizar -10 itens? Você acha possível que a alteração de um atributo como este poderia afetar o funcionamento do combo? É provável que sim. Não precisamos ter acesso a atributos deste gênero para utilizar o nosso objeto combo, mas é um dado importante dentro do mesmo. Assim a declaração correta seria:

private static var MIM_ELEMENTS:Number = 1;

Agora esse atributo só é visível dentro da classe e só pode ser alterado internamente.

Vamos a outro exemplo. Temos uma classe Pessoa e um de seus atributos é sexo e seu conteúdo pode ser feminino ou masculino.

class Pessoa
{
    public var sexo:String;
    // continua o codigo
    //  ...
}
 
var p:Pessoa = new Pessoa();
p.sexo = "Maria";

A variável sexo é pública e do tipo String portanto poderia aceitar tranquilamente a String“Maria”, entretanto, isso pode – e provavelmente irá – comprometer o funcionamento do nosso objeto, tendo em vista que o sexo “Maria” definitivamente não existe. Assim, utilizamos recursos de encapsulamento de OOP para proteger o nosso objeto. Os getters e os setters.

class Pessoa
{
    private var _sexo: String;
 
    public function get sexo():String
    {
        return _sexo;
    }
 
    public function ser sexo(value:String)
    {
        value = value.toLowerCase();
        if (value == "feminino" or value="masculino")
            _sexo = value;
 
        // caso nao fosse atribuido poderia ser disparado um evento,
        // ser gerada uma excessao,
        // exibir uma mensagem de erro e por ai vai.
    }
}

No exemplo acima, garantimos que será inserido apenas um dado válido. E o getter? Bom, se você restringe o acesso a um dado e ele precise ser visto fora do objeto, é necessário disponibilizá-lo (se e somente se ele realmente precisa ser visto de fora). Mas não confunda! O getter pode conter outras coisas além do retorno do atributo.

Geralmente quando se começa a aprender OOP (seja para criar o hábito, começar a identificar necessidades, ou qualquer outra coisa que se possa querer com isso) se indica utilizar atributos privados para tudo (ou quase) e a criação de getters/setters de todos eles. Isso porque quando se vem de uma linguagem procedural se pode fazer tudo com todos os “atributos”, não existe encapsulamento e literalmente “todo mundo é de todo mundo”. Então tenta-se a criar (ou modificar) uma cultura. Se isso é certo ou não… é um jeito (e a maioria dos programadores em algum momento passam por essa fase, depois passamos a fase de nos livrar do hábito do excesso).

A identificação das necessidade da utilização desses metodozinhos vem com a experiência. É preciso saber identificar quando o usuário pode inserir um dado diretamente, quando ele pode ver um dado, quando ele pode inserir algum dado mas com restrições, quando você precisa saber se um dado é inserido, etc… Uma forma de saber isso antes de colocar a mão na massa é a modelagem. O código abaixo por exemplo é apenas uma maneira burocrática de “gerar uma variável pública”.

private var _sexo: String;
 
public function get sexo():String
{
    return _sexo;
}
 
public function set sexo(sexo:String)
{
    _sexo = sexo;
}

Então… Vamos modelar, modelar e modelar?