Dominando action script3
-
Upload
rafael-stroemdahl -
Category
Documents
-
view
1.577 -
download
0
Transcript of Dominando action script3
Dominando
Action Script 3.0
Daniel Schmitz
Versão 1.1
Atenção
Este ebook não é gratuito. Contribua com a qualidade desta obra
e com o desenvolvimento de novos livros digitais. Não
retransmita este documento. Ajude o autor a criar mais obras de
qualidade e ajude-o a pagar as suas contas
Caso tenha obtido esta obra por outros meios, você
ainda pode me ajudar realizando a compra do mesmo, no site:
www.flex.etc.br/dominando-action-script-3
Obrigado!
A minha linda esposa Jândria
Convenções utilizadas nesta obra
Dica: Informa uma dica ao leitor.
Atenção: Informa uma mensagem de atenção ao leitor.
// ......... Indica que existe código fonte, mas não será exibido porque não é
relevante naquele momento.
trace( variável ) // saída da variável As vezes, colocamos o resultado do
comando trace como comentário após as duas barras.
Negrito Indica alterações no código fonte, como por exemplo, uma nova
propriedade adicionada a um componente.
, , até São referências ao código realizadas para um melhor
entendimento. Sempre que houver estas referências existirá uma explicação
após o código.
Suporte
Acesse www.flex.etc.br para conferir as últimas novidades do livro,
capítulos extra, código fonte, etc. Você também pode enviar um email para o
autor, caso tenha dúvidas sobre esta obra.
Daniel Pace Schmitz
Twitter: @Daniel_Schmitz
Conteúdo
Capítulo 1 - Introdução .................................................................................. 13
O que é Action Script? ............................................................................... 13
Action Script 2.0 e Action Script 3.0 ......................................................... 14
O papel do Flash Player ............................................................................. 14
O Adobe Flash Builder 4 ............................................................................ 15
Instalação do Adobe Flash Builder ......................................................... 16
Obtendo uma licença gratuita – para uso não comercial ......................... 17
Criando o “Hello World” ........................................................................... 18
O comando trace() ..................................................................................... 22
Conhecendo Action Script 3.0 ....................................................................... 24
Entendendo o HelloWorld .......................................................................... 24
Variáveis ..................................................................................................... 26
Tipos de variáveis ....................................................................................... 27
String ....................................................................................................... 27
O método toString() ................................................................................ 29
Number, int e uint .................................................................................... 30
Boolean .................................................................................................... 31
Date ......................................................................................................... 31
Object ...................................................................................................... 32
Constantes .................................................................................................. 33
Escopo ........................................................................................................ 33
Visibilidade ................................................................................................ 34
Operadores ................................................................................................. 35
Precedência de operadores ...................................................................... 36
Condicionais ............................................................................................... 36
if .............................................................................................................. 37
if + else .................................................................................................... 39
If + elseif + else ....................................................................................... 39
Switch ...................................................................................................... 40
Operador ternário (ou condicional) ......................................................... 42
Loops 43
for ............................................................................................................ 43
for each ... in ............................................................................................ 44
while ........................................................................................................ 44
do ... while ............................................................................................... 45
Pulando ou saindo do loop ......................................................................... 45
Funções ....................................................................................................... 47
Função ou método? ................................................................................. 48
Parâmetros ............................................................................................... 48
Parâmetros rest (...) ................................................................................. 49
Argumento padrão ................................................................................... 50
Função anônima ...................................................................................... 51
Funções recursivas .................................................................................. 53
Try...catch...finally e throw ........................................................................ 53
Action Script 3.0 e Orientação a Objetos ....................................................... 55
Entendendo OO .......................................................................................... 55
Classe 58
Propriedades get/set .................................................................................... 62
Métodos e sobrecarga (override) ................................................................ 65
Métodos e variáveis estáticos ..................................................................... 67
Interfaces .................................................................................................... 68
Como criar uma interface ........................................................................ 68
Como associar uma interface a uma classe ............................................. 70
Aplicando mais interfaces a uma mesma classe ...................................... 72
Acoplamento e interfaces ........................................................................ 73
Classes dinâmicas ....................................................................................... 77
Conhecendo a API ......................................................................................... 79
String 79
Array 82
Date 90
Math 92
Object 93
Eventos ........................................................................................................... 96
O que é um evento? .................................................................................... 96
Definições ................................................................................................... 96
Como trabalhar com eventos ...................................................................... 97
Como criar eventos ..................................................................................... 99
Integração com o Flex .................................................................................. 105
Criando o projeto Flex .............................................................................. 105
Importando bibliotecas ............................................................................. 107
Criando sua própria biblioteca Action Script ........................................... 111
Adicionando a biblioteca no projeto Flex ................................................ 113
Como trabalhar com Action Script e MXML .......................................... 115
Como estender componentes .................................................................... 119
Aplicando padrões de projeto ...................................................................... 121
Entendendo Padrões de Projeto ................................................................ 121
Factory ...................................................................................................... 126
Singleton ................................................................................................... 132
Observer ................................................................................................... 137
Exemplo real com Observer .................................................................. 144
Combinando Padrões (Factory + Observer) ............................................. 147
Para finalizar... ......................................................................................... 155
Capítulo 1 - Introdução
A tecnologia Flash evoluiu de forma a estar presente em mais de 98% dos
navegadores no mundo todo. Inicialmente como uma tecnologia para
“desenhar animações”, o Flash evoluiu diversos conceitos, dentre eles a sua
linguagem base.
Quando falamos em linguagem base do Flash estamos dizendo Action Script.
Todas aquelas animações dos banners Flash e dos sistemas em Flex são
movidos por esta linguagem, na qual iremos aprender os seus principais
conceitos nesta obra.
O que é Action Script?
Basicamente é uma linguagem de programação, como PHP, C# ou Java. É
claro que possui suas diferenças, mas o seu padrão ECMAScript3 garante
uma certa semelhança às linguagens derivadas do C.
É um pouco óbvio que você saiba o que é Action Script, pois comprou um
livro que fala exatamente deste assunto, por isso não vamos nos aprofundar
nisso. Rapidamente, podemos dizer que o Action Script é uma linguagem que
possui uma dinâmica orientada a objetos, e possui a maioria dos comandos
que todas as linguagens de programação têm, tais como o if, while e for.
Além disso, o Action Script possui funcionalidades extras, graças à rica
biblioteca de recursos do Flash Player. Dentre elas podemos destacar:
Carregar imagens bitmap;
Executar som e vídeo;
Capturar o som do microfone e o vídeo da webcam;
Desenhar linhas, figuras (retângulo, elipse...), fazer preenchimentos
com gradiente e aplicar máscaras, definir transparência;
Carregar e manipular dados XML;
Interagir com o usuário capturando eventos como o click do mouse ou
teclas do teclado;
Realizar animações em objetos;
Trabalhar com dados binários;
Além desse “básico”, utilizando as bibliotecas do Flash e do Flex, centenas
de novas funcionalidades são adicionadas ao Action Script.
Dica: Quer conhecer todas as classes do Action Script? Apresentamos então o melhor caminho para isso, se chama “Action Script 3.0 Reference” e está disponível neste endereço:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/index.html
Action Script 2.0 e Action Script 3.0
Existem basicamente duas versões do Action Script: 2.0 e 3.0. A versão 2.0
está ligada às versões antigas do Flash, e por isso não serão vistas nesta obra.
Estaremos abordando exclusivamente a versão 3.0 do Action Script, e quando
você ler “Action Script” em nosso livro, saiba que é “Action Script 3.0”.
O Action Script 3.0 já é usado no Flash CS 5 e sempre foi usado no Flex,
então não existe a necessidade de analisarmos diferenças entre estas duas
versões.
O papel do Flash Player
O Action Script 3.0 é a base para os sistemas em Flex e as animações no
Flash. Mas como estes códigos podem ser executados em um navegador web
se o usual é HTML e JavaScript? Para isso existe um plugin chamado Flash
Player, que atualmente está na versão 10.0.
Felizmente o Flash Player é um plugin consagrado no mercado, presente em
mais de 98% dos navegadores no mundo. É um produto maduro, com mais
de 10 anos de existência.
O Flash Player funciona como uma máquina virtual, assim como no Java. Ou
seja, quando criamos códigos para o Flash Player, não estamos preocupados
com a versão do sistema operacional ou com o navegador do cliente, pois
estamos criando código para o Flash Player. Ele será responsável em
reproduzir o nosso código de acordo com o sistema operacional/navegador.
Isso é muito bom, porque podemos programar a vontade sem a necessidade
de testar navegadores (algo necessário com JavaScript).
O Flash Player trabalha com um arquivo compilado chamado swf. Ou seja,
todos os arquivos que criarmos com a extensão “.as”, ao compilarmos, resulta
em um único arquivo swf. Se este processo é complexo para você, fique
tranqüilo, utilizando uma IDE como o Flash Builder, teremos tudo isso sem
maiores problemas. Falaremos do Flash Builder no próximo capítulo.
Além do Flash Player poder executar todo o seu código, ele também contém
um conjunto de códigos prontos que podemos usar a vontade, chamado de
API do FlashPlayer. Por exemplo, se quisermos obter uma instância da
webcam do usuário (para tirar um foto), será pela API do Flash Player que
conseguiremos isso. O mesmo vale para capturar as teclas do teclado ou o
click do mouse.
Dica: Quer ver todas as classes pertencentes ao Flash Player, juntamente com o Flex? Acesse http://flex.org/poster e faça o download.
O Adobe Flash Builder 4
O Flash Builder 4 é a melhor IDE para aprendermos Action Script. Uma IDE
(Integrated Development Enviroment – Ambiente integrado de
desenvolvimento) é um programa de computador que traz diversas
facilidades para desenvolvermos código.
Relembrando, o Flash Player, bem como o Action Script e todo o framework
Adobe Flex são gratuitos. Ou seja, você pode construir aplicações em
Flash/Flex sem gastar nada, bastando apenas usar um editor de códigos e o
compilador. Este não será o nosso caso. Nesta obra, iremos utilizar o Flash
Builder – que é uma IDE paga, mas possui 60 dias de avaliação, e ainda
possui versões gratuitas para estudantes e para pessoas que estejam
financeiramente sem recurso (iremos mostrar como conseguir isso ainda
neste capítulo).
Ou seja, o Flash Builder é a IDE perfeita para você que está aprendendo
Action Script (ou Flex), e será a IDE perfeita quando você começar a criar
aplicações em Flex.
Instalação do Adobe Flash Builder
Para instalar o Adobe Flash Builder 4, acesse o seguinte endereço:
http://www.adobe.com/go/try_flashbuilder
Na página que surge, escolha a versão “English | Standalone Windows”, e
clique em “Download Now”. Talvez seja necessário realizar um login no site
da Adobe. Se você ainda não tiver uma conta, deverá criar uma.
Após realizar o download, execute o arquivo de instalação
(FlashBuilder_4_LS10.exe ou semelhante) e extraia os arquivos para algum
diretório em seu computador. Após extrair os arquivos, acesse o diretório e
execute o arquivo Set-up.exe. Surge então um assistente de instalação, que irá
lhe guiar neste processo. Após instalar o Flash Builder, execute-o. Surgirá
uma tela de licença de software, no qual exibe quantos dias restantes você
tem de direito de acesso, além de outra tela para que você, caso deseja, possa
se conectar na conta da Adobe.
Após instalado, a tela inicial do Flash Builder é exibida conforme a figura a
seguir, onde temos na janela principal chamada de “Start Page” com alguns
tutoriais sobre Flex. No lado esquerdo, temos a janela “Package Explorer”,
que mostra o seu projeto juntamente com diversas funcionalidades que serão
vistas a medida que formos apresentando os exemplos sobre Action Script.
Na parte inferior, a janela problemans, que exibe possíveis erros no seu
código.
É válido lembrar que quando o Adobe Flash Builder 4 é instalado, o plugin
do Flash Player (versão 10) também é instalado, inclusive na versão Debug,
que exibe erros de execução de código.
Obtendo uma licença gratuita – para uso não comercial
Para obter a licença gratuita do Flash Builder, você pode realizar um cadastro
no site da Adobe como estudante (Escolas e faculdades podem obter licenças
para os seus laboratórios também), no seguinte endereço:
http://www.adobe.com/devnet/flex/free/index.html
Acessando esta página, basta preencher o cadastro e aguardar um email com
a licença. Repare que no campo “Reason for applying”, você pode usar o
item “unemployed”, que significa desempregado.
Criando o “Hello World”
Vamos usar o Adobe Flash Builder para criamos o nosso primeiro código.
Através dele iremos aprender diversos conceitos, mesmo sem conhecer
profundamente a linguagem.
Com o Flash Builder aberto, navegue até o item de menu File (canto superior
esquerdo), New, Action Script Project:
Veja que temos também projetos do Adobe Flex e do Flash Professional. Ao
selecionarmos ActionScript Project, surge a seguinte tela:
Nesta tela, devemos inserir o nome do projeto, no campo “Project Name”.
Deixe marcado o item “Use default location”, e deixe a versão do Flex SDK
como a padrão (default). Clique no botão “Finish” para criar o projeto.
Neste momento o projeto “HelloWorld” é criado, e temos a seguinte tela:
Vamos olhar novamente para o Package Explorer. Agora temos o projeto
HelloWorld criado, juntamente com algumas pastas. Neste momento,
podemos olhar somente para a pasta src, onde temos o arquivo
HelloWorld.as, que representa a classe HelloWorld.
A aba Outline mostra o “esqueleto” da classe HelloWorld, onde poderemos
navegar através dos métodos e propriedades da classe com mais facilidade.
Na janela principal, temos a classe HelloWorld, com pouco mais de 10 linhas
de código.
Vamos inserir algum código nesta classe. Na linha 9 (da figura anterior),
temos um método chamado HelloWorld. Inicialmente, vamos aceitar que este
método sempre será executado quando executarmos a aplicação. Vamos
inserir o seguinte código:
package
{
import flash.display.Sprite;
public class HelloWorld extends Sprite
{
public function HelloWorld()
{
trace("Hello World");
}
}
}
No método HelloWorld, em , usamos o comando trace para imprimir
alguma coisa na janela de Debug, chamada de Console. Este comando será
muito utilizado, e merece algumas considerações importantes
O comando trace()
O comando trace() é usado para imprimir informações na janela Output do
Flash Builder . Uma consideração muito importante sobre o trace(), é que
ele funciona somente quando executamos a aplicação no modo Debug.
O modo Debug é um modo de execução especial do Flex, que é executando
clicando no ícone em destaque da figura a seguir:
Este ícone está localizado na parte superior do Flash Builder. Vamos então
executar o projeto Hello World. Clique no ícone e aguarde o Flex abrir o
navegador web. Porque isso? Por que o Flash Builder irá executar a aplicação
HelloWorld através do arquivo HelloWorld.swf, que deverá ser executado no
navegador. Assim que o navegador abrir, volte ao Flash Builder e verifique
se a janela “Console” está aberta, com a linha “Hello World” sendo exibida,
conforme a figura a seguir:
Veja na parte inferior do Flash Builder que a aba Console ficou ativa, e na
primeira linha temos o “Hello World” que foi criado através do comando
trace(“Hello World”). Se a aba Console não apareceu automaticamente,
acesse o menu Window, Console. Se não apareceu o “Hello World”,
possivelmente você clicou no boto run ( ) e não no botão Debug ( ).
Lembre!! O comando trace() funciona somente no modo Debug
Talvez você possa estar se perguntando, porque o HelloWorld não surgiu no
navegador? Isso acontece por que o comando trace() apenas envia um
comando para o debugger informando o texto que está em seu parâmetro, e
somente isso. O trace() em nenhum momento cria uma algo visual na tela.
Conhecendo Action Script 3.0
Neste capítulo iremos aprender todos os conceitos básicos sobre o Action
Script, e sobre programação em geral. Nossa primeira tarefa será entender
melhor como o exemplo HelloWorld funciona e a partir dele compreender
diversos conceitos sobre programação.
Entendendo o HelloWorld
O programa HelloWorld que criamos no capítulo anterior apresenta algumas
particularidades até agora desconhecidas. Vamos novamente olhar para o
código do HelloWorld:
package
{
import flash.display.Sprite;
public class HelloWorld extends Sprite
{
public function HelloWorld()
{
trace("Hello World");
}
}
}
Este código é o padrão para qualquer tipo de programa ActionScript puro.
Quando criamos um programa em Flash, Flex ou Air, não estaremos mais
seguindo esse código, mas é interessante conhecer a estrutura de um
programa Action Script puro.
O código inicial começa com a definição o Package (). Um Package é um
recurso do Action Script para separar diversas classes em pacotes distintos,
assim como acontece no Java, PHP (versão 6) ou C#. Imagine que cada
classe em um sistema seja um livro. O sistema todo é uma biblioteca. Agora
imagine que o package seja a categoria dos livros (suspense, literatura, exatas
etc.) e veja que, um sistema sem packages é como uma biblioteca sem
prateleiras ordenadas pelas categorias dos livros.
Na prática, os packages separam as classes pelo seu comportamento. Classes
que manipulam datas podem ficar no package utils.data, enquanto que
controles personalizados podem ficar em controls ou components.
Em , temos o comando import. Este comando é usando para importar uma
classe de um determinado package. Isso tem que ser feito porque a linguagem
ActionScript não sabe onde estão todas as classes e todos os packages do seu
sistema, e ficaria lento ler todos eles quando o seu programa estivesse sendo
executado. Em estamos importando a classe Sprite do package
flash.display, para que possamos usá-lo em algum momento na nosso
programa.
A classe Sprite é uma classe bastante básica do Action Script 3, e sua
principal funcionalidade é conter uma lista de controles visuais que são
usados quando o programa é carregado. Para os usuários Flash, a classe
Sprite pode ser definida como uma classe semelhante a classe MovieClip,
mas sem o timeline (proveniente de animações do Flash).
Em , temos a definição da classe HelloWorld, que estende de Sprite.O
conceito de estender está ligado a orientação a objetos, e podemos pensar que
a classe HelloWorld possui a mesma característica da classe Sprite.
Em , temos a criação de um método chamado HelloWorld. Este método
possui o mesmo nome da classe, e por isso ele será chamado assim que a
classe HelloWorld foi criada. Chamamos esse comportamento como “método
construtor”.
Em , temos o nosso programa, que no momento usa apenas o comando
trace(). Nas explicações a seguir, sobre todos os conceitos iniciais do Action
Script, você poderá inserir o código no método construtor do Hello World,
para analisar os resultados. Não estaremos criando projetos para cada
exemplo, mas sim adicionando os exemplos no HelloWorld.
Variáveis
Para um computador, uma variável é apenas um espaço de memória
reservado, no qual podemos adicionar algum valor. Para nós, uma variável
pode ser considerada como um lugar onde podemos guardar dados, sejam
eles dados numéricos, um texto qualquer ou outro objeto. Estes dados são
referenciados através de um nome que damos a ele, e também através do seu
tipo (se é texto ou número).
No Action Script 3.0, definimos uma variável da seguinte forma:
var nomeDaVariavel : String;
A criação de uma variável o Action Script 3.0 é determinada pela palavra
reservada var, seguido do nome da variável, e do seu tipo.
Atenção: Uma palavra reservada é uma palavra que não pode ser usada para dar nome a variáveis, classes ou métodos. Exemplos: var, if, classe etc.
Uma lista completa de palavras reservadas está neste link:
http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3_Flex/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f9b.html#WS5b3ccc516d4fbf351e63e3d118a9b90204-7f6e
Você pode definir o nome da variável com algo relacionado ao seu contexto.
Por exemplo, se você quer armazenar o nome da pessoa, a variável
“nomeDaPessoa” é muito melhor que “x”. Pode-se perceber que a primeira
letra do nome da variável é em minúsculo, sendo que as outras letras iniciais
que compõem a palavra são em maiúsculo. Isto é apenas uma convenção que
pode ser ditada pela sua equipe ou projeto. Neste caso, estou usando a
convenção do próprio Action Script, localizado neste endereço:
http://opensource.adobe.com/wiki/display/flexsdk/Coding+Conventions
Atenção quanto a nome de variáveis, pois nomeDaVariavel é diferente de
NomedaVariavel. Chamamos isso de Case Sensitive, isto é, letras maiúsculas
diferem de letras minúsculas.
Lembre: O nome que você vai dar a sua variável é melhor que qualquer tipo de comentário. Seja coerente e use letras maiúsculas para o início de cada palavra. Veja a diferença entre valorExtornoCliente e valorextornocliente ou vlr_ext_cli
Agora que criamos a variável, como podemos relacioná-la a um valor? Isso
pode ser feito de duas formas distintas:
var nomeCliente : String = "Daniel";
ou
var nomeCliente : String;
nomeCliente = "Daniel";
Nestes dois casos, o efeito é o mesmo. Podemos definir um valor para uma
variável logo na sua declaração, ou então definir o seu valor após a
declaração. Não precisa ser na linha imediatamente a seguir, pode ser em
qualquer lugar desde que a variável criada exista naquele contexto.
Tipos de variáveis
No exemplo anterior criamos uma variável do tipo String (texto). Existem
dezenas de tipos prontos para serem usados. Os mais importantes são: String,
int, Number, Date, Object, Array.
String
Como mencionado, o String é usado para trabalhar com textos. Na verdade, o
tipo String é uma classe String, que também possui métodos e propriedades.
Isso quer dizer que o exemplo a seguir é perfeitamente possível:
var nomeCliente : String = "Daniel";
trace(nomeCliente.charAt(0));
trace(nomeCliente.toUpperCase());
Ou seja, quando criamos uma variável do tipo String podemos interagir com
esta variável. No Flash Builder, por exemplo, temos uma vasta lista de
métodos que podem ser usados para os mais diferentes tratamentos,
acessados através da complementação de código (ctrl+espaço):
Assim como em outras linguagens, o tipo String aceita caracteres de escape.
Um caractere de escape é formado por uma contra barra, seguida de uma
letra. No código a seguir, usamos um caractere de escape para quebrar um
texto em duas linhas:
var textoGrande : String = "O rato roeu a roupa \n do rei
de roma";
trace(textoGrande);
O resultado deste código é:
O rato roeu a roupa
do rei de roma
Veja que o \n (new line) quebrou o texto em duas partes. Outros caracteres de
escape são:
Caractere Resultado
\b Backspace
\n New line – Nova linha, como no exemplo anterior
\t Tab
\unnnn Insere caracteres especiais no formato Unicode
\unn Insere caracteres especiais no formato Unicode
\‟ Insere ‟
\” Insere ”
\\ Insere uma barra \
O método toString()
Este método especial está presente em todas as classes do Action Script. Ele é
usado para converter qualquer tipo de objeto (lembre-se: uma data do tipo
Date é um objeto, e um número do tipo Numeric também). O método
toString é muito usado para exibir informações rápidas sobre determinados
objetos, ajudando no debug.
O exemplo a seguir imprime uma data formatada para texto, ao invés de
imprimir a data utilizando o tipo Date:
var hoje:Date = new Date();
trace(hoje);
trace(hoje.toString());
trace(hoje.toDateString());
trace(hoje.toTimeString());
Resultado:
Mon Aug 30 21:12:35 GMT-0300 2010
Mon Aug 30 21:12:35 GMT-0300 2010
Mon Aug 30 2010
21:12:35 GMT-0300
Este exemplo possui saídas idênticas no primeiro e segundo trace. Isso
acontece porque o comando trace trata internamente o objeto realizando o
toString. Nas outras saídas, mostramos outros formatos de saída para as
variáveis que são do tipo Data.
É possível implementar o próprio toString(), em qualquer classe do Action
Script 3.0, bastando apenas criar o método e usar o operador override, veja:
package
{
import flash.display.Sprite;
public class HelloWorld extends Sprite
{
public function HelloWorld()
{
trace(this.toString());
}
public override function toString() : String
{
return "toString: " + super.toString();
}
}
}
Resultado:
toString: [object HelloWorld]
Override significa sobrescrever, então estamos sobrescrevendo o método
toString da classe HelloWorld. Veja que no método construtor chamamos o
próprio toString da classe, e ainda usamos o toString da classe Sprite através
do comando super, que chama propriedades e métodos da classe pai. Iremos
rever estes conceitos no tópico sobre orientação a objetos.
Number, int e uint
A representação de números pode ocorrer em formatos distintos. Isso
acontece porque um número pode ser negativo ou positivo, pode ter casas
decimais ou não. Para cada característica, existe um tipo diferente. O tipo
mais comum é o Number, um formato de 64 bits que aceita números
positivos e negativos, além de números fracionados.
O tipo int possui 32 bits e aceita números positivos e negativos, mas não
aceita números fracionados, apenas números inteiros. O tipo uint aceita
apenas números inteiros e positivos. O valor máximo e mínimo de cada um
deles pode ser definido através do seguinte código:
trace(uint.MIN_VALUE); //0
trace(uint.MAX_VALUE); //4294967295
trace(int.MIN_VALUE); //-2147483648
trace(int.MAX_VALUE); //2147483647
trace(Number.MIN_VALUE); //4.9406564584124654e-324
trace(Number.MAX_VALUE); //1.79769313486231e+308
O uso de cada um destes tipos varia de acordo com a necessidade. É válido
lembrar que usar o tipo Number abrange todos os tipos numéricos, mas
perde-se em performance, pois estamos alocando um espaço desnecessário na
memória.
Quando criamos uma variável do tipo Number, e não atribuímos nenhum
valor a ela, o seu valor padrão é NaN, que significa invalido ou indefinido.
Os tipos int e uint assumem o valor 0 quando são criados.
Boolean
As variáveis booleanas podem assumir dois valores: true ou false. No
momento em que criamos uma variável booleana, ela ainda está com o valor
padrão false, e somente torna verdadeira se houver a atribuição.
var isNew : Boolean;
trace(isNew); // false
isNew = true;
trace(isNew); // true
Date
Campos do tipo Date são feitos expressamente para manipular uma data fixa,
sendo que uma data é composta do dia/mês/ano e da hora:minuto:segundo,
não necessariamente nesta seqüência. A criação de uma data pode ser feita da
seguinte forma:
var agora:Date = new Date();
trace(agora); // Fri Sep 3 15:02:06 GMT-0300 2010
Também é possível criar datas a partir de datas existentes, como no exemplo
a seguir:
var ontem:Date = new Date("09/02/2010");
trace(ontem); // Thu Sep 2 00:00:00 GMT-0300 2010
Como pode-se ver no exemplo acima, criamos uma data no formato
americano: MM/DD/AAAA. Pode-se criar datas em outros formatos, como
no exemplo a seguir:
var data:Date = new Date("01 Sep 2009");
trace(data); // Tue Sep 1 00:00:00 GMT-0300 2009
Como também podemos ver uma data em diferentes formatos:
var hoje:Date = new Date();
trace(hoje);
trace(hoje.toString());
trace(hoje.toDateString());
trace(hoje.toTimeString());
Atenção: O formato DD/MM/AAAA é setado automaticamente através da propriedade formatString dos componentes de data do Flex.
Object
Esta classe é considerada a classe base de todas as outras classes da
hierarquia do Action Script. Pode-se criar um objeto qualquer da seguinte
forma:
var pessoa:Object = new Object();
pessoa.Nome = "Daniel";
pessoa.Telefone = 222111;
trace(pessoa); // [object Object]
trace(pessoa.Nome); // Daniel
trace(pessoa.Telefone); // 222111
O mesmo objeto criado acima poderia ser criado também da seguinte forma:
var pessoa:Object = {Nome:'Daniel',Telefone:111222}
trace(pessoa); // [object Object]
trace(pessoa.Nome); // Daniel
trace(pessoa.Telefone); // 222111
Constantes
Uma constante é uma variável especial que define um valor que nunca é
alterado. A constante é definida pela palavra const, conforme o exemplo a
seguir:
package
{
import flash.display.Sprite;
public class HelloWorld extends Sprite
{
public const PI:Number = 3.14;
public function HelloWorld()
{
trace(PI); // 3.14
}
}
}
Usamos o PI apenas de exemplo. Você pode usar o valor de PI através da constante Math.PI
Escopo
A definição do escopo de uma variável influencia mais na legibilidade do
programa do que na memória em si. Antigamente era até notável definir o
escopo de uma variável para manter um nível de memória aceitável, mas hoje
em dia o principal propósito é definir somente uma região onde a variável
será vista.
O escopo pode ser definido como a parte do programa em que uma variável
está ativa. Geralmente a variável está ativa somente no pedaço de bloco de
código em que foi criada. Observe o código a seguir e as variáveis a,b e c.
package
{
import flash.display.Sprite;
public class HelloWorld extends Sprite
{
protected var a:String;
public function HelloWorld()
{
var b:String;
if (true)
{
var c:String;
}
}
}
}
Em , temos a variável a que está disponível em toda a classe HelloWorld.
A variável b () está disponível somente no método construtor da classe.
Uma particularidade da linguagem Action Script é que a variável c não está
definida apenas dentro do bloco if, mas em todo o método construtor
também. Isso acontece porque o Action Script 3.0 não possui escopo a nível
de blocos, mas apenas a níveis de funções. Isso é diferente do C#, C++ e
Java.
Visibilidade
Outro conceito importante é a visibilidade de uma variável, método ou classe.
Você já deve ter percebido, por diversas vezes, a palavra “public” nas classes
e em métodos. No exemplo sobre Escopo, vimos em uma variável que ao
invés de public, é protected. Estes “modificadores” definem a visibilidade da
variável dentro da classe, ou de um método ou da própria classe. A
visibilidade determina até quanto uma variável está visível para uso em
relação a outra classe. Os modificadores do Action Script 3.0 são:
public: disponível em todo o momento. Uma classe fica disponível
para todo o sistema, assim como suas variáveis e métodos públicos.
private: usado em variáveis dentro de uma classe. As variáveis ficam
disponíveis somente para aquela classe, e nenhuma outra.
protected: As variáveis ficam disponíveis para a classe e suas sub-
classes (herança).
internal: As classes ficam disponíveis somente no seu package.
Os modificadores acima possuem algumas incompatibilidades. Por exemplo,
não faz sentido criar uma classe protected, ou uma classe private.
Operadores
Os operadores no Action Script 3.0 são usados para os mais diversos fins.
Existe uma semelhança grande com o padrão de operadores de qualquer
linguagem baseada no ECMAScript3, como php, C#, Java. A seguir
apresentamos os operadores mais comuns.
Atribuição (=) : Basicamente é usado para atribuir um valor a uma
variável.
Soma,subtração,multiplicação, divisão (+,-,*,/): Conhecidos para
efetuar cálculos básicos em variáveis numéricas. O operador +
também pode concatenar caracteres.
Módulo (%): Usado para se obter o “resto” de uma divisão. Por
exemplo, 5/3 retorna o resto 2. Ou seja, 5%3 é igual a 2.
Potência (^) : Eleva um valor a potência de outro. Por exemplo: 5^2
significa 5 elevado a 2.
Incremento (++) e Decremento(--): Usado para somar ou subtrair
um valor de 1. Neste caso a precedência do operador define o seu
comportamento. Supondo que temos a variável i com o valor 2:
o trace(i++) : Vai retornar 2, e depois somar 1
o trace(++i): Vai somar 1, e retornar 3
Nota do autor: Poucas foram as vezes que usei ++i. Se isso gerar confusão, é melhor fazer i++ antes de usar a variável i.
Operadores compostos ( +=, -=, *=, /=, %=): São usados para
compor as variáveis de acordo com o operador. O exemplo explica
tudo:
o x += 1 é o mesmo que x = x+1
o x -= z é o mesmo que x = x – z
o y *= 10 é o mesmo que y = y * 10
Precedência de operadores
O que fazemos quando temos o seguinte cálculo:
var i : Number = 10 + 5 - 3 * 2;
Ou seja, o valor 10 será somado ao 5, que será subtraído por 3, ou por 6? Ou
então 10 será somado com 4? As possibilidades são muitas e neste caso existe
uma procedência em relação aos operadores. Esta procedência diz que
operadores de multiplicação e divisão são calculados antes dos operadores de
soma e multiplicação. Então teremos algo: 10+5-6 = 9.
Dica: Não confie na precedência de operadores. Defina parêntesis, sempre!
Os parêntesis são usados para forçar a precedência. É recomendável sempre
usar parêntesis para separar claramente uma operação de cálculo complexa.
Exemplo:
var i : Number = 10 + 5 - 3 * 2 / 7 * 9 - 5 ^ 2;
var j : Number = ( (10 + 5 - 3 * 2) / (7 * 9 - 5) )^2 ;
Condicionais
Uma condicional é uma estrutura de desvio de código, usada amplamente em
qualquer programa de computador. Uma condição é formada por uma
expressão que pode retornar falso ou verdadeiro. Dependendo do resultado, o
fluxo de código dentro do programa será alterado.
As condições são feitas através de operadores, sendo os mais utilizados
apresentados a seguir
Igualdade ( == ): Usado para comparar um valor com o outro.
Cuidado com a diferença entre igualdade e atribuição
Desigualdade ( != ): O sinal de interrogação indica negação, sendo
que != indica “é diferente de”.
Maior, menor, maior ou igual, menor ou igual: Caracterizado pelos
símbolos >, <,>=,<=. Indicam, por exemplo, se um valor é maior que
o outro.
Pode-se unir diversas condições através dos operadores AND (&&) e OR (||).
Por exemplo, idade > 10 && idade <= 20, indica uma faixa de 11 a 20.
Como já foi dito, uma condição sempre retorna verdadeiro ou falso. Mas
existem tipos especiais que, dependendo de seu valor, são convertidos
automaticamente para verdadeiro/falso. Por exemplo, quando temos um
objeto qualquer e usamos ele para uma condição, esta resultará em verdadeiro
se este objeto não for nulo. Quando testamos números inteiros temos um
comportamento semelhante. Se o número for diferente de zero, a expressão
será convertida em verdadeiro, caso contrário, falso. Para strings, uma
condição será falsa somente se a string estiver vazia. Veremos melhor as
condições após o entendimento do if.
if
A condicional mais famosa existente é o if (em português: se). Esta condição
executa o seu bloco de código se a expressão avaliada retorna verdadeiro. O
exemplo a seguir ilustra este processo:
var idade:uint = 25;
if (idade>18)
{
trace("Você é maior de idade");
}
if (idade<18)
{
trace("Você é menor de idade");
}
Neste código, definimos a variável idade com o valor 25. O primeiro if avalia
se idade é maior que 18. Se for, o fluxo de código entra em . No segundo if,
quando avaliamos se idade é menor que 18, o código em não será
executado, pois a condição idade<18 retorna falso.
Em relação a condições especiais, podemos citar objetos, números e texto. O
exemplo a seguir ilustra melhor estas condições:
//Objetos:
var obj:Object = {Nome:"Daniel"}
if (obj)
trace("obj não é nulo");
//Numeros
var idade:uint = 0;
if (idade)
{
if (idade<18)
{
}
//....... continua
}
else
trace("você ainda não nasceu");
//String
var str:String = "";
if (str)
trace(str)
else
trace("preencha algo para str");
Atenção: Neste exemplo podemos perceber ifs sem o uso de chaves. Isso somente é possível quando o bloco dentro do if contiver uma única linha. Se forem duas ou mais, o uso do parêntesis é obrigatório.
Atenção 2: Em concursos públicos/provas de programação, o uso de ifs sem chave e identado é uma pegadinha que você se confudir, então tome cuidado: if (condição) faça A faça B faça C
Neste if, o bloco B e C serão executados independentemente da condição. Cuidado!!
if + else
O código anterior usou dois ifs que poderiam ser substituídos pelo if/else
(se/então). O else é usado para executar o bloco de código quando a condição
do if for falsa. O exemplo anterior pode ser resumido a:
if (idade>18)
{
trace("Você é maior de idade");
}
else
{
trace("Você é menor de idade");
}
If + elseif + else
Pode-se testar várias condições através do elseif, como no exemplo a seguir:
var idade:uint = 50;
if (idade < 18)
{
trace("Você é menor de idade");
}
else if (idade < 60)
{
trace("Você é maior de idade");
}
else if (idade < 150)
{
trace("Você é idoso");
}
else
trace("Você está morto!");
Este exemplo, um pouco mais extenso, mostra algumas particularidades do
if/elseif/else. Inicialmente pode-se perceber que o bloco de cada if é
executado apenas uma vez. Vou explicar melhor, quando temos idade igual a
50, o primeiro if falha e caímos então no primeiro else if, cuja a condição é
idade<60. Este bloco será executado, e somente ele. O próximo if possui a
condição idade<120, que também satisfaz quando idade=50, mas como o
bloco já foi executado no if anterior, este if nem será avaliado. Em outras
palavras, quando temos um bloco com vários elseif, somente um será
executado. No final do código temos o else que será executado se nenhuma
condição de todos os else ifs for aceita. No exemplo, o else será executado se
a idade for maior ou igual a 150.
Quanto o uso de else/if torna-se algo extremamente extenso, podemos usar
outra condição para facilitar o entendimento do código. Esta condição se
chama switch.
Switch
O switch é usado basicamente para testar diversos valores, facilitando a
leitura em relação ao else if. O exemplo a seguir ilustra este processo:
var hoje:Date = new Date();
var dia:Number = hoje.day; //dia da semana
switch (dia)
{
case 0:
trace("domingo");
break;
case 1:
trace("segunda");
break;
case 2:
trace("terça");
break;
case 3:
trace("quarta");
break;
case 4:
trace("quinta");
break;
case 5:
trace("sexta");
break;
case 6:
trace("sábado");
break;
}
A sintaxe do switch é formada pelo uso do case, seguido de um valor, e o uso
do break, para que quando o valor especificado for encontrado, o bloco do
case seja executado e ao encontrar o break, seja finalizado.
Pode-se usar vários cases com o mesmo bloco de código, por exemplo:
var hoje:Date = new Date();
var dia:Number = hoje.day; //dia da semana
switch (dia)
{
case 0:
case 6:
trace("descançar!!");
break;
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
trace("trabalhar!!");
break;
}
O código acima é perfeitamente válido. Também podemos obter um resultado
semelhante com a condição default. Veja:
switch (dia)
{
case 0:
case 6:
trace("descançar!!");
break;
default:
trace("trabalhar!!");
break;
}
O bloco da condição default é executado somente quando todos os cases não
satisfazerem a condição proposta.
Operador ternário (ou condicional)
Este operador foi criado especialmente para verificações rápidas onde
escrever um bloco if é muito custoso. O seu uso está atribuído ao caractere ?
(interrogação) e ao : (dois pontos), formando a seguinte sintaxe:
(condição) ? “verdadeiro” : “falso”
Nesta sintaxe, temos inicialmente a condição, que será testada e caso seja
verdadeira, o primeiro mini bloco (antes dos :) será executado. Se a condição
for falsa, o segundo mini bloco (após os :) será executado. O exemplo a
seguir ilustra este processo:
(dia==0||dia==6) ? trace ("descançar") : trace
("trabalhar");
//ou:
trace( (dia==0||dia==6) ? "descançar" : "trabalhar");
Pode-se usar o operador ternário para atribuições, como no exemplo anterior,
onde o resultado seria a entrada do parâmetro do comando trace().
A seguir outro exemplo perfeitamente válido:
var info:String;
info = (dia==0||dia==6) ? "descançar" : "trabalhar";
trace(info);
Atenção: O operador ternário deve ser usado com cautela, somente para expressões extremamente simples.
Loops
Um loop é uma forma de percorrer um determinado código várias vezes. As
formas de se criar um loop cresceram de acordo com o avanço das linguagens
de programação, sendo as mais conhecidas o for e o while.
for
O for é a forma mais conhecida para criar um loop, definido-se um intervalo
de valores que condizem com a quantidade de vezes que você quer executar o
bloco do código. O loop for possui a seguinte estrutura:
for (var i:int = 0;i<100;i++)
{
//fazer algo
}
Veja que o for possui três parâmetros, separados por “;” - que definem a
quantidades de vezes que o loop será realizado. O primeiro parâmetro cria
uma variável chamada de i (geralmente usamos as letras i,j,k para loops) que
é do tipo int e possui o valor inicial igual a 0.
O segundo parâmetro do for atribui uma condição que mantém o loop
funcionando. Enquanto a condição estiver sendo satisfeita, o loop continuará
rodando. O terceiro parâmetro indica o incremento da variável i, neste caso a
cada finalização do bloco do for, a variável será incrementada em uma
unidade.
Pode-se usar o for para a interação em um Array:
var arr:Array = ["um","dois","tres"];
for (var i:int = 0;i<arr.length;i++)
trace(arr[i]);
Neste exemplo, veja que a condição do laço está dependente do número de
itens do array (), e que conseguimos obter o item atual utilizando a
variável i ().
for each ... in
O último exemplo era muito utilizado em linguagens mais antigas. A única
forma de percorrer um array era através do loop for. Felizmente, com o
avanço das linguagens de programação, o uso do for each (para cada) tornou-
se uma forma mais simples de percorrer um Array. Veja o mesmo exemplo
anterior, agora com for each:
var arr:Array = ["um","dois","tres"];
for each (var item:String in arr)
trace(item);
Veja que o for each interage através de cada item do array, atribuindo a cada
laço o valor na variável item. Use o for each sempre quando for trabalhar
com dados provenientes de um Array ou de um XMLList, pois é mais fácil
de entender e codificar.
Dica: Se for fazer um for each com um objeto que é ArrayList ou ArrayCollection, use o atributo source ao invés de usar o próprio ArrayList. Veja um exemplo: var arr:ArrayList = new ArrayList(["um","dois","tres"]);
for each (var item:String in arr.source)
trace(item);
while
O loop while é usado sempre quando não conhecemos ao certo a quantidade
de vezes que o bloco de código será repetido. Por exemplo, podemos usar o
while para remover uma quantidade de itens de um array. Isso não seria
possível com o for porque a quantidade de itens está sempre mudando, pois
quando retiramos um item do array, a sua propriedade length é alterada.
Por exemplo:
var arr:Array = ["um","dois","tres"];
while (arr.length>0)
{
trace(arr[arr.length-1] + " será excluido");
arr.pop();
}
trace("arr nao possui mais items!");
Neste exemplo, o while faz o loop do código enquanto houver itens no array.
Veja que, se a condição inicial do while não for satisfeita, o bloco de código
do while não será executado.
do ... while
O loop do ... while tem a particularidade de executar o bloco de código do
while pelo menos uma vez. Isso acontece porque a condição somente é
testada no final do loop. O exemplo a seguir ilustra este processo:
var num:int = 10;
do
{
num++;
}while (num < 10)
trace(num); // 11
Pulando ou saindo do loop
Existem dois comandos: continue e break que são responsáveis em,
respectivamente, pular o loop ou então sair do loop. Eles são úteis quando
queremos verificar, por exemplo, se o loop não está entrando em laço
infinito, ou se devemos necessariamente pular para o próximo item. Por
exemplo, suponha que você tenha um array de números, e que precise fazer
um processamento muito grande nelas, dentro de um loop. Mas no código só
há interesse em números pares, e você não quer realizar o processamento em
números ímpares. O código a seguir ilustra o problema:
var arr:Array = [1,2,3,4,5,6,7,8,9]
for each (var numero:int in arr)
{
if (numero%2 != 0) continue;
trace("Fazendo calculo para: " + numero);
}
Neste código usamos o módulo de 2 para sabermos se o número é impar. Se
for, a instrução continue é chamada e o processamento “pula” para o próximo
item. O resultado deste código é:
Fazendo calculo para: 2
Fazendo calculo para: 4
Fazendo calculo para: 6
Fazendo calculo para: 8
Veja que usamos um if com o seu código na mesma linha, e é claro que o
código a seguir também funciona:
if (numero%2 != 0)
{
continue;
}
Mas fica feio pra caramba!! (nota do autor)
O break é usado para pular completamente o loop, indo para a próxima linha
após o loop. Suponha que o número 0 é uma instrução para parar
completamente o processamento:
var arr:Array = [1,2,3,4,5,6,0,8,9]
for each (var numero:int in arr)
{
trace("Analisando: " + numero);
if (numero == 0)
{
trace("defeito na máquina de leitura. programa
abortado");
break;
}
if (numero%2 != 0) continue;
trace("Fazendo calculo para: " + numero);
}
O resultado deste programa é:
Analisando: 1
Analisando: 2
Fazendo calculo para: 2
Analisando: 3
Analisando: 4
Fazendo calculo para: 4
Analisando: 5
Analisando: 6
Fazendo calculo para: 6
Analisando: 0
defeito na máquina de leitura. programa abortado
Funções
As funções foram criadas ainda na época da linguagem estruturada e tinham a
principal finalidade de reutilizar partes do código que eram executadas mais
de uma vez. Com o passar do tempo, as funções ficaram cada vez mais
importantes e passaram também a separar melhor o código da aplicação, de
forma que tornasse mais legível.
Basicamente, uma função em Action Script 3.0 possui a seguinte sintaxe:
public function fazAlgumaCoisa(a:String):Boolean
{
return true;
}
Analisando a sintaxe, temos inicialmente a definição da Visibilidade da
função. Ela pode ser public, protected, private ou internal. Depois temos a
palavra reservada function seguido do nome da função. Após o nome da
função, temos a definição dos parâmetros de entrada, o nome da variável e do
seu tipo. Pode-se criar quantos parâmetros forem necessários. No final da
criação da função temos a definição do tipo de retorno que a função assume.
Neste caso, a função retorna algo booleano. Quando a função não for retornar
nada, inserimos void:
public function fazAlgumaCoisa(a:String):void
{
//não retorna nada....
}
Função ou método?
Existe uma separação conceitual em função e método. Uma função é criada
fora do escopo de uma classe. Um método nada mais é que uma função
dentro de uma classe. Você vai perceber que quando formos abordar o
conceito de orientação a objetos, a palavra função será substituída por
método, mas a sintaxe é a mesma.
Parâmetros
Os parâmetros passados para a função são globais dentro da função como um
todo. Você pode usá-los e modificá-los, mas precisa tomar cuidado quando
modificar seus valores, pois eles podem não ser refletidos em toda a sua
classe.
Explicando melhor, tomamos o exemplo a seguir:
public function HelloWorld()
{
var raio : int = 10;
alteraRaio(raio);
trace(raio); // 10
}
public function alterarRaio(raio:int) : void
{
raio = raio + 5;
}
A saída do comando trace() é 10, e não 15. Isso acontece porque quando
chamamos o método alterarRaio() passamos a variável raio por valor.
Quando passamos algo por valor, estamos clonando a variável e apenas
repassando o seu valor. Ou seja, o conteúdo na memória é duplicado e a
variável original não altera o valor. Isso sempre acontece com variáveis do
tipo primitivos String, Number, int, uint e Boolean.
Agora vamos a um novo exemplo, passando um objeto ao invés de um tipo
primitivo:
public function HelloWorld()
{
var circulo : Object = {Raio:10};
alteraRaio(circulo);
trace(circulo.Raio); // 15
}
public function alteraRaio(circulo:Object) : void
{
circulo.Raio = circulo.Raio + 5;
}
Agora criamos um objeto chamado circulo que possui a propriedade Raio.
Quando passamos este objeto como parâmetro da função alteraRaio(),
estamos passando esse parâmetro por referência. Isso significa que a
referência do objeto (o ponteiro que aponta para o endereço de memória do
objeto) é repassada, e que ao alterar as propriedades desse objeto, as
alterações serão mantidas. Chamamos esta passagem de “passagem por
referência”, e isso sempre acontece quando repassamos um objeto por
parâmetro de uma função.
Parâmetros rest (...)
Existe uma particularidade na criação de parâmetros chamada “rest”, que
indica uma série de parâmetros que são automaticamente transformados em
um array de dados.
A idéia básica do rest é possibilitar que o usuário possa adicionar quantos
parâmetros forem necessários. Por exemplo, suponha que você deseja criar
uma função chamada “média”, e deseja fazer com que o usuário possa
chamar a função da seguinte forma:
Media(10,5);
Media(1,2,3,4);
Media(10,10,10,10,10,10);
Pode-se ver que a quantidade de parâmetros é dinâmica. Isso é feito com o
uso do parâmetro rest, representado por três pontos, veja:
public function HelloWorld()
{
trace(Media(10,2)); // 6
trace(Media(1,1,1,1,1,1,1,1,1)); // 1
trace(Media(1,2,3,4,5)); // 3
}
public function Media(... numeros:Array) : Number
{
var total:Number=0;
for each (var item:Number in numeros)
total += item;
return total/numeros.length;
}
Neste exemplo, o uso dos três pontos em indica que os parâmetros
passados são armazenados em um array chamado números.
Argumento padrão
No Action Script 3.0 pode-se passar um argumento padrão para cada
argumento de uma função. Os argumentos podem ter valores padrão através
do sinal de atribuição igual, veja:
public function HelloWorld()
{
SayHello("Daniel"); // Olá Daniel
SayHello(); // Olá Anônimo
}
public function SayHello(nome:String="Anônimo") : void
{
trace("Olá " + nome);
}
Pode-se criar quantos argumentos padrão forem necessário, mas para que
possamos omiti-lo da função, eles precisam estar sempre nos últimos
parâmetros. Ou seja, os parâmetros opcionais devem estar sempre após os
parâmetros obrigatórios. O exemplo a seguir provoca um erro de compilação:
public function Say(nome:String="eu",cid:String) : void
O correto seria:
public function Say(cid:String, nome:String="eu") : void
Função anônima
Uma função anônima é aquela que não tem nome (óbvio!!). Ela é declarada
implicitamente no código e geralmente está atrelada a algum evento. A
criação de uma função anônima é feita da seguinte forma:
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
public class HelloWorld extends Sprite
{
public function HelloWorld()
{
this.stage.addEventListener(Event.ADDED, function(event:Event):void
{
trace("função anônima chamada");
}
);
}
}
}
Neste código, criamos uma função anônima como um parâmetro de uma
outra função (addEventListener). Isso é perfeitamente possível no Action
Script 3.0 (e em algumas linguagens como o JavaScript). Repare o destaque
dos parêntesis em vermelho, indicando que a função realmente é um
parâmetro.
Existem algumas diferenças das funções declaradas e das funções anônimas.
As anônimas existem somente no escopo que foram definidas, não podem ser
reutilizadas (até um certo, podem – mas dentro do escopo).
Talvez você esteja se perguntando qual a melhor, a resposta é depende do que
você quer fazer. As funções anônimas são muito usadas nos eventos
associados a algum componente do Action Script 3.0, quando estes eventos
não precisam usar métodos reusáveis.
Nos métodos anônimos, cuidado com o uso do this, pois ao invés dele
“apontar” para o objeto da classe em que foi chamado, ele aponta para a
própria função. Veja no código a seguir as saídas do comando trace:
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
public class HelloWorld extends Sprite
{
protected var teste:String = "blablabla";
public function HelloWorld()
{
trace("1 " + this.toString());
trace("2 " + teste);
this.stage.addEventListener(Event.ADDED,
function(event:Event):void
{
trace("3 " + this.toString());
trace("4 " + teste);
trace("5 " + this.teste);
});
}
}
}
Saída:
1 [object HelloWorld]
2 blablabla
3 [object global]
4 blablabla
5 undefined
Veja que o item 5 do trace retorna indefinido, pois o this não refere-se a
classe HelloWolrd e sim a função anônima. Veja que, para acessar a classe,
você deve omitir o uso do this. Este é um pequeno detalhe de escopo que
deve ser lembrando quando criarmos funções anônimas.
Funções recursivas
As funções recursivas são aquelas que chamam a si próprias. O exemplo
típico deste tipo de função é o cálculo do fatorial de um número:
public function HelloWorld()
{
trace(fatorial(5));
}
public function fatorial(num:int):int
{
trace("fat de: " + num);
if (num==0)
return 1;
else
return (num * fatorial(num-1) );
}
O resultado deste código é:
fat de: 5
fat de: 4
fat de: 3
fat de: 2
fat de: 1
fat de: 0
120
Try...catch...finally e throw
O uso do try...catch é comum na maioria das linguagens de programação e se
bem empregado pode trazer uma melhoria significativa no código. Use-o
sempre quando for realizar alguma operação que possa resultar em um erro
provável, como a leitura de um arquivo XML, ou salvar/converter alguma
imagem.
O throw é usado justamente para disparar um erro, e pode ser usado em
conjunto com o try. Por exemplo, quando executamos este código:
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
public class HelloWorld extends Sprite
{
public function HelloWorld()
{
throw new Error("Disparei um erro");
}
}
}
Ao executarmos este código, temos a seguinte saída:
A janela do “Adobe Flash Player 10” aparece porque quando você instalou o
Flash Builer 4, o assistente instalou ao Flash Player na versão debug. Para
pessoas “normais” – digo, “não desenvolvedores”, que instalam o Flash
Player na versão normal, esta janela não será exibida. Aliás, não será exibido
nada, pois a política da Adobe é não incomodar o usuário “leigo” com janelas
de erro.
Action Script 3.0 e Orientação a Objetos
A programação orientada a objetos é uma gradativa substituição da
programação estruturada. Ela ganhou força a partir de 1990, mas já era
abordada desde 1960. Atualmente a maioria das linguagens de programação
são OO (OO – orientada a objetos) .
Entendendo OO
A principal proposta da linguagem orientada a objetos é modelar o mundo
real. No mundo real temos uma infinidade de exemplos de objetos
interagindo entre si. Podemos considerar um carro como um objeto, que
possui propriedades físicas como peso, largura e altura, consumo de
combustível, etc. Também podemos considerar ações tais como acelerar,
frear e buzinar. Além destas duas características, temos também os eventos
que o carro produz. Por exemplo, se o nível de combustível estiver baixo,
emitir um sinal, ou então, se a força aplicada sobre os freios for muito forte
ao ponto derrapagem, aliviar a força nos freios.
Resumindo, um objeto no mundo tem propriedades físicas, tem ações e eventos.
Além disso, um objeto no mundo real interage com outro objeto. Por
exemplo, o objeto carro possui o objeto motor. Uma ação do objeto carro
(acelerar) pode desencadear uma série de ações do objeto motor, como
aumentar a combustão e dessa forma interferir em uma propriedade do carro,
como a velocidade. Ou seja, está tudo interligado e se no mundo real tudo
funciona bem, porque não aplicar isso na programação?
Desta forma a OO copia estes conceitos e tenta aplicá-los na programação.
Então um objeto que possui ações, na verdade são métodos. Propriedades
físicas são como variáveis globais, sem falar dos eventos e dos
relacionamentos existentes.
Assim, quando pensamos em OO em um nível básico, estamos criando
classes (que formarão o objeto), métodos, variáveis e eventos. Assim como
no mundo real, onde cada objeto possui o seu segmento (você não vai no
açougue comprar um carro, vai?), no mundo OO temos também pacotes ou
packages, onde podemos dividir grupos de classes com funcionalidades
comuns.
Além do básico, a OO possui outros conceitos tais como interfaces, herança e
sobrecarga de métodos. Todos eles serão comentados neste capítulo. Além
disso, (mais??) existem os padrões de projeto que usam vários conceitos
relativos a OO, para uma melhor estruturação do projeto.
Alguns conceitos devem estar bem esclarecidos para quem está começando a
trabalhar com OO, e iremos numerá-los a seguir:
Objeto: Um objeto é algo “vivo” dentro do seu programa, é algo que
você pode manipular suas propriedades e métodos. No mundo real,
um objeto é o carro construído e funcionando normalmente. Ou seja,
assim como você tem vários carros em uma cidade, você pode ter
vários objetos do tipo carro em seu programa.
Classe: Uma classe é o esqueleto do objeto. É o que define como as
propriedades e métodos vão se comportar. No mundo real, uma classe
é o projeto do carro ainda na prancheta, mostrando todo o seu
funcionamento em teoria.
Instância: Um objeto é a instância de uma classe. Quando
instanciamos uma classe, estamos criando o objeto e funciona como
se estivéssemos pegando todos os dados do carro na prancheta e
criando o carro fisicamente.
Tipo: Não existe somente um carro no mundo. Existem milhares de
tipos de carros. Uns são esportivos, outros econômicos e outros de
corrida. Um tipo de objeto é comumente chamado de classe. Ou seja,
se temos um objeto cuja classe é “Carro”, chamamos o tipo deste
objeto de “Carro”.
Baseado nestes conceitos, podemos olhar novamente a classe HelloWorld,
criada no início desta obra:
package
{
import flash.display.Sprite;
public class HelloWorld extends Sprite
{
public function HelloWorld()
{
trace("Hello World");
}
}
}
A classe HelloWorld define o tipo “HelloWorld”. Quando o programa for
executado, a classe HelloWorld será instanciada, tornando-se um objeto, e
será anexada ao programa. A definição formal de uma classe será vista no
próximo capítulo.
Um dos conceitos importantes sobre OO é a herança. Ou seja, podemos criar
uma classe com um determinado comportamento e criar uma outra classe que
possui um comportamento semelhante, mas existem diferenças em sua
totaliadade. Por exemplo, a classe Pessoa é a classe pai da classe Aluno e da
classe Professor. No exemplo HelloWorld, a classe herda da classe Sprite,
que representa uma classe do Action Script 3.0, então a classe HelloWorld,
mesmo vazia, já possui diversas variáveis e métodos.
Classe
A definição formal de uma classe no Action Script 3.0 é a seguinte:
public class HelloWorld
Geralmente cada classe no projeto pode ser representada por um arquivo.
Você não precisa conhecer profundamente como criar uma classe da forma
correta porque o próprio Flash Builder te ajuda nisso.
Lembre-se: Você está usando o Flash Builder, use-o para criar as classes que precisa
Então para criar uma classe no nosso projeto HelloWorld, vamos até File,
New, ActionScript Class. O assistente surge conforme a figura a seguir, na
qual iremos criar a classe Carro, que será definida no package veiculos.
Assim que criamos a classe, temos no Flash Builder a seguinte configuração:
Podemos perceber, no Package Explorer, o package veiculos seguido da
classe Carro. O código, no lado direito, é um pouco diferente da classe
HelloWorld, pois agora não existe herança.
Atenção: Não há sentido em criar a classe carro herdando de Sprite, porque carro não tem absolutamente nada a ver com visualização do programa no navegador.
Uma propriedade comum a qualquer carro é a sua cor. Então podemos criar
uma variável global chamada cor, alterando o código da classe para:
package veiculos
{
public class Carro
{
public var cor:String;
public function Carro()
{
}
}
}
Agora para demonstrar o conceito de herança, vamos criar o carro Ferrari no
package esportivos. Novamente, usando o assistente do Flash Builder, temos:
Um detalhe agora é o campo SuperClasse, na qual definimos a classe Carro
como a classe pai de Ferrari. Você vai reparar que quando começamos a
digitar Carro, surge um popup com sugestões das classes que você tem
disponível.
Após criar a classe, temos:
Veja no Package Explorer que a classe Ferrari está no package esportivos, e
no código temos a definição da classe com o uso do extends para definir uma
herança. Bom, se existe a herança, a variável Cor tem que estar presente na
classe Ferrari, e podemos alterar o nosso código para:
package veiculos.esportivos
{
import veiculos.Carro;
public class Ferrari extends Carro
{
public function Ferrari()
{
cor = "Vermelho";
super();
}
}
}
Ok, existem Ferraris de outras cores, mas vamos apenas entender o conceito de hernaça.
Veja no construtor da classe Ferrari que temos a palavra super() (), que
chama o construtor da classe Carro. Esta é uma particularidade do Action
Script 3.0, usado sempre quando você quer chamar o construtor da classe pai.
Para finalizar o conceito sobre classes e herança, vamos voltar a classe
HelloWorld e instanciar a classe Ferrari, veja:
package
{
import flash.display.Sprite;
import veiculos.esportivos.Ferrari;
public class HelloWorld extends Sprite
{
public function HelloWorld()
{
var meuCarro:Ferrari = new Ferrari();
trace("O meu carro tem a cor: " +
meuCarro.cor);
}
}
}
Propriedades get/set
O conceito de propriedade vai um pouco além do conceito de variável. No
exemplo anterior, criamos uma variável pública chamada “cor”. Até este
momento era aceitável isso, mas suponha que você deseja que somente as
classes filhas de Carro pudessem alterar a sua cor. Como resolver isso?
Relembrando o tópico sobre visibilidade, podemos alterar a visibilidade da
variável cor para protected. É uma ótima saída, mas gera um efeito colateral.
Como podemos exibir a cor do carro se a classe HelloWorld não pode mais
acessá-la? Neste problema podemos criar um método chamado GetCor(), que
retorna a cor do carro. Então temos as seguintes modificações:
package veiculos
{
public class Carro
{
protected var cor:String;
public function Carro()
{
}
public function GetCor():String
{
return cor;
}
}
}
e:
package
{
import flash.display.Sprite;
import veiculos.esportivos.Ferrari;
public class HelloWorld extends Sprite
{
public function HelloWorld()
{
var meuCarro:Ferrari = new Ferrari();
trace("O meu carro tem a cor: " +
meuCarro.GetCor());
}
}
}
Esta solução funciona, e funcionou por muito tempo na programação até os
programadores perceberem que estavam criando centenas de
getAlgumaCoisa, deixando o código sujo.
Dica: porque esconder a variável cor? Este exemplo é apenas um exemplo, claro. Mas bons programadores sabem que quanto menos variáveis estiverem disponíveis em suas classes, melhor. Isso ajuda na manutenção futura e na prevenção de erros. Quer ver um exemplo no mundo real? Suponha que você pudesse alterar a taxa de combustão do motor na hora que bem entendesse. Melhorar a performance do carro seria uma possibilidade bem remota então resta a conclusão que provavelmente você irá emitir mais poluentes e danificar o motor.
Como solução do problema de esconder as variáveis do mundo externo e de
diminuir a quantidade de getAlgumaCoisa no seu código, o uso de
propriedades get/set passou a tornar-se uma boa alternativa, resultando em
um código mais limpo e fácil de entender.
Sempre chamamos de propriedade, uma variável que possui get e set.
A sintaxe básica para esse propriedade é:
private var _cor:String;
public function get Cor():String
{
return _cor;
}
public function set Cor(value:String):void
{
_cor = value;
}
A sintaxe básica é essa. Criamos uma variável private chamada _cor. O uso
do underscore é apenas uma forma de dizer que aquela variável “segura” um
get/set.
Depois criamos dois métodos. Repare que estes métodos têm as palavras get
e set, antes do nome da propriedade. O conteúdo destes métodos manipula
internamente a variável _cor.
Para resolver o problema proposto, em que somente as classes filhas
poderiam alterar a cor do carro, mas todas as outras poderiam acessar a
propriedade, alteramos a visibilidade do get para private, e deixamos a
variável _cor como protected.
Você pode usar o Flash Builder para criar a propriedade automaticamente
para você. Por exemplo, ao criar a variável _cor, com ela selecionada, acesse
o menu de contexto (botão direito do mouse) e navegue até Source, Generate
Getter/Setter.
Métodos e sobrecarga (override)
Métodos criados em uma classe podem ser sobrecarregados, ou seja, podem
ser redefinidos pelas classes filhas. Por exemplo, suponha que exista um
cálculo que determina o valor de um carro. Suponha que o padrão é uma
constante vezes a quantidade de rodas, então teríamos:
package veiculos
{
public class Carro
{
public function Carro()
{
_rodas = 4;
}
protected var _cor:String;
public function get Cor():String
{
return _cor;
}
protected var _rodas:uint;
public function get Rodas():uint
{
return _rodas;
}
public function obterPreco():uint
{
return Rodas*10000;
}
}
}
e:
package veiculos.esportivos
{
import veiculos.Carro;
public class Ferrari extends Carro
{
public function Ferrari()
{
_cor = "Vermelho";
super();
}
override public function obterPreco():uint
{
return 50*super.obterPreco();
}
}
}
O uso da palavra override indica que o método está se sobrepondo ao método
do pai. Podemos inclusive acessar o método do pai através do uso da palavra
super seguido do método desejado.
O código de HelloWorld fica desta forma:
package
{
import flash.display.Sprite;
import veiculos.esportivos.Ferrari;
public class HelloWorld extends Sprite
{
public function HelloWorld()
{
var meuCarro:Ferrari = new Ferrari();
trace("O meu carro tem a cor: " +
meuCarro.Cor);
trace("O preço é: " +
meuCarro.obterPreco());
}
}
}
A saída é:
O meu carro tem a cor: Vermelho
O preço é: 2000000
Métodos e variáveis estáticos
Um método estático é um tipo especial de método que não necessita que a
classe esteja instanciada para que seja usada. Por exemplo, a classe Math,
usada para cálculos matemáticos, não necessita necessariamente que seja
instanciada para que seus métodos sejam usados. Veja:
trace(Math.sin(30));
Os métodos estáticos são chamados diretamente pela classe. Para criar um
método estático, usamos a palavra chave static. Por exemplo:
package veiculos.esportivos
{
import veiculos.Carro;
public class Ferrari extends Carro
{
//......
public static function Buzinar():String
{
return "foooom";
}
}
}
e:
trace(Ferrari.Buzinar());
O mesmo pode ser usado, por exemplo, com uma constante:
public static const BUZINA:String = "Foom";
e:
trace(Ferrari.BUZINA);
Constantes estáticas são muito usadas em eventos, que serão vistos mais
adiante.
Interfaces
Existem dois estágios de entendimento sobre interfaces: Ou você adora elas,
e as usa em seu s programas, ou você nunca entendeu direito o seu propósito
e nem usa. Ok, podem existir outros estágios, mas provavelmente você já
passou pelo menos por algum dos dois primeiros comentados.
Uma interface funciona como um contrato no mundo real. Um contrato no
mundo real diz que as obrigações que tem que cumprir, como por exemplo
um contrato de emprego, dizendo que você tem que desenvolver software e
não pode ficar de papo no MSN. Assim, uma interface diz a uma classe o que
ela deve fazer, e nada mais.
As interfaces são úteis também quando queremos definir um comportamento
comum entre diversas classes existentes, mas estas classes não possuem um
ancestral.
Voltando ao nosso exemplo de carros, suponha que agora estamos criando
um programa para seguradoras e que nesse meio temos carros, lanchas e
helicópteros. Também temos casas e temos seguros relativos a pessoas.
Como programador, você logo pensa: ok, não dá pra criar uma classe pai para
todo mundo, pois os objetos possuem comportamentos completamente
diferentes, mas mesmo assim todos eles possuem uma taxa de juros em caso
de atraso no pagamento, então como eu posso resolver esse problema de
forma a não prejudicar a legibilidade do meu código?
A resposta é com o uso de interfaces, ou seja, como todas as classes distintas
(e que não possuem a mesma classe Base) possuem comportamentos que
podem ser padronizados, o uso de interface pode ser empregado.
Como criar uma interface
Com o Flash Builder, navegue até File, New, ActionScript Interface. Crie a
interface ITaxa no package taxas. Neste momento temos o seguinte código:
Para criamos um método que será comum a todas as classes que
implementarem ITaxa, adicionamos o seguinte código:
package taxas
{
public interface ITaxa
{
function getVariacaoTaxa():Number;
}
Ou seja, criamos o método getVariacaoTaxa(), mas não implementamos
nada. A interface nunca implementa nada, apenas diz quais são os métodos e
propriedades que devem ser implementados.
Como associar uma interface a uma classe
Agora vamos aplicar esta interface na classe Carro. O código fica:
package veiculos
{
import taxas.ITaxa;
public class Carro implements ITaxa
{
//............
public function getVariacaoTaxa():Number
{
return 0.02;
}
}
}
O uso do implements diz que a classe implementa uma interface. Então, se a
classe implementa ITaxa, ela deve declarar e implementar o método
getVariacaoTaxa.
Agora supondo a classe Pessoa, que não tem nada a ver com Carro,
implementando a mesma interface:
Veja que ao criar a classe Pessoa, podemos adicionar a interface a ser usada,
fazendo com que o método seja criado automaticamente. O código gerado
pelo Flash Builder é o seguinte:
package pessoas
{
import taxas.ITaxa;
public class Pessoa implements ITaxa
{
public function Pessoa()
{
}
public function getVariacaoTaxa():Number
{
return 0;
}
}
}
Basta alterar o valor do método getVariacaoTaxa(). No programa principal,
podemos criar a instância das duas classes e usar a interface para obter o
valor da taxa, veja:
package
{
import flash.display.Sprite;
import pessoas.Pessoa;
import taxas.ITaxa;
import veiculos.Carro;
public class HelloWorld extends Sprite
{
public function HelloWorld()
{
var tiposSegurados:Array = [new Pessoa(),new
Carro()];
for each (var segurado:Object in
tiposSegurados )
{
if (segurado is ITaxa)
{
trace((segurado as
ITaxa).getVariacaoTaxa());
}
}
}
}
}
Neste exemplo criamos um array com as duas classes e depois usamos um for
each para percorrer cada uma das classes e obter o valor da taxa, fazendo um
typecast – uma conversão simples de tipo.
Aplicando mais interfaces a uma mesma classe
Você pode criar interfaces para “marcar” uma classe com um determinado
comportamento. Por exemplo, suponha que você queira marcar as classes que
possuem seguro com ISegurado. A interface ISegurado não possui nenhum
método, e serve apenas para marcar o comportamento de uma classe:
package seguro
{
public interface ISegurado
{
}
}
e:
package veiculos
{
import seguro.ISegurado;
import taxas.ITaxa;
public class Carro implements ITaxa, ISegurado
{
//.......
}
}
Podemos criar e implementar quantas interfaces forem necessárias.
Dica: É bom ter um correto entendimento de interfaces pois elas ajudam muito na maioria do entendimento dos padrões de projeto existentes.
Acoplamento e interfaces
Outra utilidade das interfaces é gerar diminuir o acoplamento entre as classes.
Acoplamento é o quanto uma classe é dependente da outra. Por exemplo,
suponha que a roda de carro fosse soldada. Não existem parafusos que
retiram a roda, e se o pneu furar, você perde o carro.
Esse forte acoplamento provoca diversos problemas. Inicialmente ninguém
vai querer comprar um carro assim, e se comprar, não poderá trocar o pneu
nunca. Para resolver este problema (ainda estamos no mundo real), existe
uma interface entre a roda e o carro. O carro deve possuir uma entrada para
parafusos no diâmetro correto, além de espaçamento idêntico as entradas da
roda.
Veja que o carro não tem conhecimento da roda em si, e que apenas sabe que
deve implementar aquela estrutura para que algo como uma roda seja
acoplada.
As interfaces possuem exatamente este comportamento. Uma classe apenas
conhece a interface, e não mais a outra classe que está acoplada. Voltando a
OO, suponha que tenhamos agora a classe motor. Todo carro precisa da
classe motor, então podemos criar:
package motores
{
public class Motor
{
public function Motor()
{
}
}
}
E que agora, o carro implemente a classe motor, criando um forte
acoplamento:
package veiculos
{
import motores.Motor;
import seguro.ISegurado;
import taxas.ITaxa;
public class Carro implements ITaxa, ISegurado
{
protected var motor:Motor;
public function Carro()
{
_rodas = 4;
motor = new Motor();
}
//.......
Veja que, neste caso, criamos um forte acoplamento entre duas classes. Ou
seja, uma classe não vive sem a outra. Suponha que, em um determinado
estágio do programa (por exemplo, após 6 meses de uso), você precise criar
derivações do motor, pois agora existem motores de carro e motores de
lancha. Você precisará manualmente alterar a classe Carro, e fazer as
modificações, possivelmente gerando um acoplamento entre Carro e
MotorCarro. Isso é muito ruim para o seu programa, pode gerar problemas e
bugs que antes não existiam e pode inclusive alterar o comportamento normal
de suas classes.
Quando programamos de olho nas modificações futuras, sabemos que não
podemos criar esse forte acoplamento e usamos interfaces para resolver o
problema. A interface faz com que a classe Carro use apenas as
funcionalidades do motor que ela precisa, ou seja, a classe Carro agora
conecta-se a interface do motor e não ao motor propriamente dito.
Vamos ao código então, inicialmente criando a interface IMotor:
package motores
{
public interface IMotor
{
function Acelerar():void;
}
}
E vamos implementar essa interface na classe Motor:
package motores
{
public class Motor implements IMotor
{
public function Motor()
{
}
public function Acelerar():void
{
//aqui o código para acelerar
}
}
}
Finalmente, a classe Carro agora acoplada a uma interface e não mais a uma
classe:
package veiculos
{
import motores.IMotor;
import motores.Motor;
import seguro.ISegurado;
import taxas.ITaxa;
public class Carro implements ITaxa, ISegurado
{
protected var motor:IMotor;
public function Carro(motor:IMotor)
{
this.motor = motor;
_rodas = 4;
}
//...........
Veja que criamos uma propriedade Motor na classe Carro, mas que até este
momento ainda não instanciamos ou criamos alguma referência entre o carro
e o motor. Nesse contexto entra outro conceito, de deixar que a
implementação da classe atribua o motor, ou seja, assim que instanciamos a
classe Carro, passamos a instância do motor em si, veja:
package
{
import flash.display.Sprite;
import motores.Motor;
import veiculos.Carro;
public class HelloWorld extends Sprite
{
public function HelloWorld()
{
var carro:Carro = new Carro(new Motor());
}
}
}
Com isso podemos criar um carro com diversos tipos de motores, mas
somente aqueles que possuírem a interface IMotor. Independente da classe de
motor que existir, o que vale para a classe carro é a interface IMotor, gerando
um acoplamento fraco entre a classe Carro (e suas derivadas) e a classe
Motor.
Pensamento do leitor: Até entendi, mas onde vou aplicar isso no meu dia a dia? Não faço a mínima idéia!! Resposta: Todos sabemos que estamos direcionando nossos estudos ao Flex e que se a sua necessidade é apenas criar formulários para inserir dados e criar Grids para exibir dados, não faz sentido se preocupar com interfaces. Mas quando vamos além disso, como por exemplo gerenciar as janelas abertas na aplicação, ou processos ocorrendo no sistema, o uso de interfaces começa a tornar-se interessante. Idéias onde as interfaces nos ajudam: 1) Em um sistema de restaurante, você deseja exibir visualmente as mesas que estão desocupadas, além de exibir mesas que estão fechando a conta. Se você criar um sistema fortemente acoplado, quando o seu chefe pedir um novo status, por exemplo, quando o cliente da mesa faz o pedido, possivelmente você terá problemas em inserir esse comportamento no seu sistema. Com o pensamento mais aberto e implementando interfaces, possivelmente você apenas definirá uma nova classe, por exemplo MesaFazendoPedido, que implementa uma interface chamada IDesign que possui o método CorPadrao, e mais algumas interfaces que o sistema precisa, e pronto, você terá a funcionalidade pronta sem muito custo.
2) Você está usando as classes do MDIWinodow do Flex, e como lá existem muitos métodos e você quer apenas usar alguns, você pode usar uma interface para filtrar estes métodos. A interface se chama IJanela, de forma a ficar fácil a manipulação de alguns comandos como minimizar ou fechar.
Bom, estes são apenas alguns exemplos. Somente a prática mesmo para ver
que vale a pena. Lembre-se que todo o esforço e dedicação ao uso de padrões
de projeto e interfaces é destinado a melhorar a manutenção do seu código,
como o seu entendimento. Você estará programando para sofrer menos no
futuro, e não para criar código mais rápido no presente. Veremos mais a
respeito de padrões de projeto no último capítulo desta obra.
Classes dinâmicas
O Action Script permite que uma classe seja dinâmica, possibilitando que
novas variáveis possam ser inseridas dinamicamente em tempo de execução.
Para tornar uma classe dinâmica, basta inserir a palavra dynamic na criação
da classe, confirme o exemplo a seguir:
public dynamic class Carro implements ITaxa, ISegurado
{
protected var motor:IMotor;
//........
Dessa forma, podemos, em tempo de execução, adicionar propriedades a
classe, como neste exemplo:
public function HelloWorld()
{
var carro:Carro = new Carro(new Motor());
carro.Nome = "Fox";
carro.Kms = 4000;
carro.Cidades = new Array();
(carro.Cidades as Array).push("Rio de
Janeiro", "São Paulo", "Cabo frio");
trace("Meu carro: " + carro.Nome);
trace("Kms rodados: " + carro.Kms);
trace("Cidades:");
for each (var cidade:String in carro.Cidades)
trace(" " + cidade);
}
O resultado seria:
Meu carro: Fox
Kms rodados: 4000
Cidades:
Rio de Janeiro
São Paulo
Cabo frio
Conhecendo a API
Agora que já compreendemos o básico da Orientação a Objetos, podemos
começar a usufruir das principais vantagens de uma linguagem moderna e de
um rico conjunto de classes para nos auxiliar nas tarefas mais comuns, como
manipular Strings, Datas, fazer buscas em XML e Arrays, manipular
ByteArrays etc.
Este capítulo trata especialmente dos principais métodos das principais
classes do Action Script. É necessário conhecer esses métodos para agilizar o
desenvolvimento em si e para que não seja necessário realizar
implementações que já foram criadas pelo time da Adobe.
String
A classe String trata especialmente de texto, e possui as seguintes
propriedades/métodos:
length
Determina a quantidade de caracteres que forma a string.
charAt
Retorna o caractere de acordo com o índice passado. Para obter o último
caractere, pode-se usar a propriedade: length()-1
concat
Usado para concatenar strings, realizando as conversões quando necessário.
Ou seja, não é preciso chamar o método toString para números ou datas.
Exemplo:
var nome:String = " Daniel "
trace(nome.concat(" Schmitz, nasceu em ", new
Date(1980,0,11) ) ) ;
Resultado:
Daniel Schmitz, nasceu em Fri Jan 11 00:00:00 GMT-0200
1980
indexOf
Usado para obter o índice de um segmento de texto no texto principal. É
comumente usado para sabermos se uma string contém algum caractere
específico, como no exemplo a seguir:
var expressao:String = "Daniel|[email protected]"
if ( expressao.indexOf("|") != -1)
{ //Existe o |
//aqui o código para quebrar a string em 2 pedaços
}
Veja que se a string procurada não for encontrada, o retorno será -1. Então
para verificarmos se uma string contém uma expressão, basta comparar com
o valor -1.
lastIndexOf
Tem o mesmo efeito do indexOf, mas busca uma string no texto partindo da
direita para a esquerda.
match
Faz uma comparação na string e retorna um array de strings que “batem” com
a comparação. Esta comparação é feita através de uma expressão regular. O
exemplo a seguir faz com que os emails que estão separados por vírgula
sejam adcionados a um Array.
var expressao:String = "[email protected],
var emails:Array =
expressao.match(/\w*@\w*\.[org|com|com.br]+/g);
for each(var email:String in emails)
trace(email);
Resultado:
replace
Realiza a troca de uma expressão por outra. A expressão de busca é realizada
através de expressão regular. Suponha que em um texto você queria trocar
“Brazil” por “Brasil”:
var texto:String = " Estamos no Brazil agora ";
texto = texto.replace(/Brazil/,"Brasil");
trace(texto); // Estamos no Brasil agora
search
Busca por uma expressão no texto, retornando o índice do primeiro caractere
que “bater” com a expressão.
split
Retorna um array de items que batem com uma expressão regular em um
texto. Por exemplo:
var texto:String = " Fulano1, Fulano2, Fulano3 ";
for each (var nome:String in texto.split(/,/))
trace(nome);
Resultado:
Fulano1
Fulano2
Fulano3
Neste exemplo inserimos o comando split dentro do for each. Isto é
perfeitamente possível já que o comando split retorna um Array.
substr
Retorna uma seqüência de caracteres dado uma posição inicial e uma
quantidade qualquer. Não é muito usada porque as expressões regulares
conseguem de certa forma suprir esta necessidade.
substring
Possui um comportamento semelhante ao substr, mas ao invés de indicar a
quantidade de caracteres a serem extraídos, informamos a posição final do
mesmo.
Por exemplo, suponha que você queira extrair o primeiro nome de uma
pessoa:
var nome:String = "Daniel Schmitz";
trace(nome.substring(0,nome.indexOf(" "))); //Daniel
toLowerCase
Converte todos os caracteres de uma string para minúsculo.
toUpperCase
Converte todos os caracteres de uma string para maiúsculo.
Array
length
Retorna a quantide de itens do Array
concat
Usado para concatenar duas ou mais arrays. Você pode juntar uma array
com dois elementos e outra com três, resultando em um array de 5
elementos.
every
Este método executa um teste em cada elemento do array. Este teste é
definido por uma função, que pode ser anônima. O método every retorna
falso se um dos itens não passar na validação.
Por exemplo, suponha que você tenha um array de emails e você quer testar
se estes emails estão válidos. Vamos supor, para facilitar, que para um email
ser válido, basta encontrar o caractere @. Então o código completo fica:
public function HelloWorld()
{
var emails:Array = ["[email protected]",
"fulano4email.com",
];
if (emails.every(checkEmail))
trace("Todos os emails ok");
else
trace("Algum email com problema");
}
public function checkEmail(email:String, index:int,
arr:Array):Boolean
{
return email.indexOf("@")!=-1;
}
filter
Possui um funcionamento semelhante ao every, mas ao invés de retornar
verdadeiro ou falso, o filter retorna um novo Array, somente com os
elementos que satisfazem a condição. Vamos aproveitar o exemplo anterior e
criar um novo exemplo, onde dado um array de elementos, obtemos somente
elementos que são emails (possuem um @):
public function HelloWorld()
{
var palavras:Array = ["[email protected]",
"daniel",
"fulano4email.com",
];
var emails:Array = palavras.filter(checkEmail);
for each ( var email:String in emails)
trace(email);
}
public function checkEmail(email:String, index:int,
arr:Array):Boolean
{
return email.indexOf("@")!=-1;
}
Resultado:
forEach
Este método executa uma função para cada item de um array. A idéia é a
mesma dos métodos anteriores.
var numeros:Array = [2,3,4,5,6];
numeros.forEach(function(numero:int, index:int,
arr:Array):void
{
arr[index] = numero * 2;
});
for each ( var numero:String in numeros)
trace(numero);
Resultado:
4
6
8
10
12
Neste exemplo temos duas novidades. A primeira é que criamos uma função
anônima, e a segunda é que usamos os outros parâmetros da função anônima,
que são o índice atual do Array (index) e o próprio Array (arr),
respectivamente. Com esses dois parâmetros adicionais, pode-se alterar os
elementos de acordo com a necessidade.
indexOf
Retorna o índice de um elemento, lembrando que o elemento procurado deve
ser idêntico ao elemento que está no array.
lastIndexOf
Comportamento semelhante ao indexOf, mas começa a busca pelo final do
array.
join
Retorna uma string que é a união de todos os elementos de um Array
separados por um delimitador pré definido. Exemplo:
var nomes:Array = ["fulano1",
"fulano2",
"fulano3",
"fulano4"
];
trace(nomes.join(", "));
Resultado:
fulano1, fulano2, fulano3, fulano4
map
Este método possui um comportamento semelhante ao forEach, com a
diferença de retornar um novo array, baseado na função executada para cada
item do array. Vamos repetir o exemplo do forEach para entendermos o seu
comportamento:
public function HelloWorld()
{
var numeros:Array = [2,3,4,5,6];
trace (numeros.map(dobrar).join(","));
}
public function dobrar(num:int,index:int,arr:Array):int
{
return num*2;
}
Resultado
4,6,8,10,12
Veja que neste código usamos o map, que retorna um array e logo depois
usamos o join para concatenar todos os itens do array em uma única string.
pop
Retira e retorna último elemento adicionado no Array.
push
Adiciona um elemento no Array.
reverse
Reverte os elementos de um array. O primeiro elemento vai para o último, e
assim por adiante.
shift
Remove e retorna o primeiro elemento adicionado no Array.
slice
Retorna uma nova array através de uma faixa de elementos de um outro
Array.
some
Executa um teste para cada elemento do array, através de uma função de
callback (como no método filter), mas quando a função de callback retorna
verdadeiro, o teste pára e o método também retorna verdadeiro. Este método
é útil para avaliar um array que possui muitos itens, e que todos os itens
tenham uma particularidade. No exemplo a seguir, temos que somar todos os
elementos de um array, mas para isso precisamos nos certificar que cada
elemento é um número. Podemos usar o some para isso, e na primeira vez
que encontrarmos um elemento que não é um número, o loop entre os
elementos restantes não será executado.
public function HelloWorld()
{
var numeros:Array = [2,3,4,5,6,'noNum',8,9,10];
if (numeros.some(isNotNum))
trace("Erro: Nem todos os elementos são números");
else
trace("Ok: Todos os elementos sao números");
}
public function isNotNum(num:*,index:int,
arr:Array):Boolean
{
trace("avaliando ",num);
return !(num is Number);
}
Resultado:
avaliando 2
avaliando 3
avaliando 4
avaliando 5
avaliando 6
avaliando noNum
Erro: Nem todos os elementos são números
sort
O método sort é usado para ordenar os elementos de um array. O uso do sort
sozinho estipula uma regra simples de ordenação, baseada no código Unicode
do carectere (Então “Z” vem antes de “a”).
Para melhorar o uso do sort, podemos criar uma função de callback que irá
avaliar os itens a serem ordenados. Esta função deverá retornar um número
inteiro, que pode ser:
-1 quando o primeiro elemento deve estar antes do segundo elemento
0 quando os elementos são iguais
1 quando o primeiro elemento deve estar depois do segundo elemento
Vamos a um exemplo simples. Suponha que tenhamos um array de objetos e
que cada objeto tenha as propriedades nome e idade. Agora suponha que
queremos ordenar estes objetos, de acordo com a idade de cada objeto do
array:
public function HelloWorld()
{
var arr:Array = new Array();
arr.push({Nome:'Fulano1',Idade:12});
arr.push({Nome:'Fulano2',Idade:32});
arr.push({Nome:'Fulano3',Idade:22});
arr.push({Nome:'Fulano4',Idade:2});
arr.push({Nome:'Fulano5',Idade:72});
arr.push({Nome:'Fulano6',Idade:52});
arr.sort(ordenaIdade);
arr.forEach(mostraIdade);
}
public function ordenaIdade(obj1:*,obj2:*):int
{
if (obj1.Idade > obj2.Idade) return 1;
if (obj1.Idade < obj2.Idade) return -1;
return 0;
}
public function
mostraIdade(item:*,index:int,arr:Array):void
{
trace(item.Nome,item.Idade);
}
sortOn
O método sortOn é usado justamente quando queremos ordenar um array de
objetos. Na verdade ele faz exatamente a mesma coisa que o exemplo
anterior, só que precisamos passar apenas a propriedade do objeto em que
queremos ordenar. Além da propriedade a ser ordenada, temos que repassar
como esse objeto será ordenado, pois para propriedades numéricas,
precisamos informar que a ordenação será numérica e não baseada no código
Unicode. O exemplo a seguir mostra como ordenar por nome e como ordenar
por idade:
public function HelloWorld()
{
var arr:Array = new Array();
arr.push({Nome:'Fulano5',Idade:12});
arr.push({Nome:'Fulano4',Idade:32});
arr.push({Nome:'Fulano3',Idade:22});
arr.push({Nome:'Fulano2',Idade:2});
arr.push({Nome:'Fulano1',Idade:72});
arr.push({Nome:'Fulano6',Idade:52});
arr.sortOn("Nome");
trace("Ordenado por nome");
arr.forEach(mostraObj);
arr.sortOn("Idade", Array.NUMERIC);
trace("Ordenado por idade");
arr.forEach(mostraObj);
}
public function mostraObj(item:*,index:int,arr:Array):void
{
trace(" ",item.Nome,item.Idade);
}
Resultado:
Ordenado por nome
Fulano1 12
Fulano2 32
Fulano3 22
Fulano4 2
Fulano5 72
Fulano6 52
Ordenado por idade
Fulano4 2
Fulano1 12
Fulano3 22
Fulano2 32
Fulano6 52
Fulano5 72
unshift
Adiciona elementos no início do Array
Date
A classe Date é usada para manipular data e hora. Geralmente instanciamos
um objeto que contém a informação sobre uma data qualquer. Podemos
repassar esta data ou então obter a data atual. A partir da data, podemos
realizar operações e obter informações sobre aquela data em questão.
Além de métodos a classe Date possui diversas propriedades que representam
alguma informação da data em si.
date
Usado para retornar o dia do mês, um valor que pode ir de 1 a 31.
dateUTC
O mesmo que a propriedade date, mas usando a data universal (universal
coordinate time). Todas as propriedades possuem a sua forma UTC, então
não será necessário comentar todas elas.
day
Retorna o dia da semana (0 é domingo e 6 é sábado).
fullYear
Retorna o ano com quatro dígitos.
hours
Retorna a hora (0 a 23).
milliseconds
Retorna os milisegundos (0 a 999).
minutes
Retorna os minutos da data (0 a 59).
month
Retorna o mês (0 a 11).
seconds
Retorna os segundos (0 a 59).
time
Representa o número de milisegundos desde 1 de janeiro de 1970.
toDateString
Retorna a representação da data sem a informação da hora
toString
Retorna uma string com a informação da data e da hora, no seguinte formato:
Wed Apr 12 15:30:17 GMT-0700 2006
toTimeString
Retorna somente a informação da hora
Math
A classe Math possui métodos específicos para o cálculo de operações
matemáticas. Além disso possui algumas constantes como o PI e o LN10
(logaritmo na base 10).
Para acessar o número PI, basta usar:
trace(Math.PI); // 3.141592653589793
Ao contrário de classes como Date, Array ou String, o Math contém um
conjunto de métodos estáticos, que são acessados diretamente pela classe,
veja:
Math.<nome do método>();
abs
Retorna o número absoluto de um número. Um número absoluto é sempre
um número positivo.
cos,sin, tan
Retorna o cosseno, seno ou tangente de um ângulo em radianos
log
Retorna o valor do logaritmo na base natural E (Math.E)
floor
Retorna o número inteiro mais próximo de um número fracionado, como por
exemplo “2.12”.
max
Retorna o maior número dado uma seqüência de números. Por exemplo:
trace(Math.max(1,2,3,4,5,3,12,5,22,7)); // 22
min
Retorna o menor número dado uma seqüencia de números
pow
Eleva um número a potência de outro número. Por exemplo:
trace(Math.pow(2,3)); // 8
random
Retorna um número randômico entre 0 e 1
sqrt
Retorna a raiz quadrada de um número
Object
A classe Object é a classe no mais alto nível do Action Script 3.0. Isso
significa que quase todas as classes do Action Script 3.0 possuem como
classe base, o Object.
hasOwnProperty
Este método retorna verdadeiro ou falso indicando se uma propriedade está
presente no objeto. Você pode usar este método para descobrir se um objeto
possui a propriedade que você precisa, principalmente em objetos dinâmicos.
No código a seguir, onde ordenamos um conjunto de objetos pela
propriedade Idade, podemos verificar pelo menos se a propriedade existe.
public function HelloWorld()
{
var arr:Array = new Array();
arr.push({Nome2:'Fulano5',Idade:12});
arr.push({Nome:'Fulano4',Idade:32});
arr.push({Nome:'Fulano3',Idade:22});
arr.push({Nome:'Fulano2',Idade:2});
arr.push({Nome:'Fulano1',Idade:72});
arr.push({Nome:'Fulano6',Idade:52});
if (!arr.some(NaoPossuiNome))
{
arr.sortOn("Nome");
trace("Ordenado por nome");
arr.forEach(mostraObj);
}
if (!arr.some(NaoPossuiIdade))
{
arr.sortOn("Idade", Array.NUMERIC);
trace("Ordenado por idade");
arr.forEach(mostraObj);
}
}
public function mostraObj(item:*,index:int,arr:Array):void
{
trace(" ",item.Nome,item.Idade);
}
public function
NaoPossuiNome(item:Object,index:int,arr:Array):Boolean
{
return !item.hasOwnProperty("Nome");
}
public function
NaoPossuiIdade(item:Object,index:int,arr:Array):Boolean
{
return !item.hasOwnProperty("Idade");
}
Resultado:
Ordenado por idade
Fulano2 2
undefined 12
Fulano3 22
Fulano4 32
Fulano6 52
Fulano1 72
propertyIsEnumerable
Indica se uma propriedade possui items que são enumeráveis. Com isso
garantimos que podemos realizar um laço for...each .
Eventos
O Action Script 3.0 é uma linguagem tipicamente orientada a eventos. Neste
capítulo, estaremos esclarecendo como associar eventos a métodos e como
criar novos eventos para suas classes.
O que é um evento?
Um evento pode ser associado a um acontecimento no sistema. Este
acontecimento possui informações, tais como quem executou o evento e
como ele está se propagando. Um exemplo de evento é quando a aplicação
está pronta para ser executada, ou seja, quando tudo que precisa ser carregado
na memória está pronto. Então a aplicação dispara um evento, informando
este acontecimento.
Outro evento é o click do botão do mouse, ou então a ação de fechar uma
janela. A maioria das classes do Action Script 3.0 possuem eventos. Por
exemplo, a classe Sprite, da qual vimos em todos os exemplos, possui os
eventos click, doubleClick, added e muitos outros! Para conhecer estes
eventos, fique de olho na API do Action Script 3.0.
Definições
Existem alguns conceitos relacionados a eventos que devemos conhecer,
sendo eles destacados a seguir:
Tipo de evento
Todo evento possui um tipo (uma classe) e todo evento herda da classe Event.
O evento criado é repassado para o método listener, que pode ser usado para
obter mais informações sobre o evento. Como o evento é uma classe, você
pode adicionar propriedades ou métodos extras na classe. Por exemplo, o
evento Click possui em suas propriedades a coordenada X e Y do mouse, no
momento em que o evento foi disparado.
Target
O target (alvo) é justamente o componente/objeto que disparou o evento. Por
exemplo, quando um botão dispara o evento mouseOver, o target é o botão.
O target mantém-se durante toda a fase de vida do evento.
Dispatcher
O termo dispatch refere-se ao disparo do evento. É realizado nas classes onde
o evento é disparado. Esse disparo é realizado através do método
dispatchEvent, um método da classe EventDispatcher.
Listener
É o objeto que se registra ao dispatcher para receber os eventos. O listener
tem o objeto de chamar o handler associado àquele evento.
Handler
É a função que é chamada quando o evento é disparado.
Como trabalhar com eventos
Agora que conhecemos o conceito de eventos, podemos usá-los através do
método addEventListener. Este método, como o próprio nome diz, adiciona
um listener a um evento, ou seja, ele relaciona um evento a um método.
Na classe HelloWorld, podemos por exemplo adicionar um listener no evento
que diz que a aplicação está pronta para uso. O código para criar esse
comportamento está definido a seguir:
package
{
import flash.display.Sprite;
import flash.events.Event;
public class HelloWorld extends Sprite
{
public function HelloWorld()
{
trace("iniciando...");
this.addEventListener(Event.ADDED_TO_STAGE,OnSpriteAddToS
tage);
trace("fim do construtor...");
}
private function
OnSpriteAddToStage(event:Event):void
{
trace("HelloWorld foi adicionado ao stage");
}
}
}
Resultado:
iniciando...
fim do construtor...
HelloWorld foi adicionado ao stage
Este código traz uma particularidade dos eventos. Eles são assíncronos.
Algo assíncrono diz que não se sabe ao certo quando vai ocorrer. No nosso
exemplo, veja que o trace dos construtores são chamados primeiro em
relação ao trace do evento. Ou seja, quando fazemos o addEventLisntener
não estamos automaticamente chamando o método OnSpriteAddToStage. Ele
será chamado somente quando o evento ADDED_TO_SATE for disparado.
Isso acontece momentos depois do construtor da classe HelloWorld ser
executado.
Como criar eventos
Agora que aprendemos a usar os eventos disponíveis nas classes do Action
Script 3.0, vamos criar o nosso próprio evento.
Imagine que você esteja criando uma classe para somar dois números, e que
deseja disparar eventos quando a soma der um resultado negativo, e quando a
soma ultrapassar o valor 100. Você quer disparar um evento também sempre
quando a soma for finalizada.
Inicialmente podemos criar a classe que conterá estes eventos. Esta classe
deverá herdar de Event. No projeto HelloWorld, vá até File, New,
ActionScript Class:
Assim que criar a classe do evento, precisamos criar algumas constantes
estáticas que definem quais são os eventos. O código até este momento é o
seguinte:
package events
{
import flash.events.Event;
public class CalculosEvent extends Event
{
public static const RESULT_OVER100:String =
"CalculosEvent_RESULT_OVER100";
public static const RESULT_NEGATIVE:String =
"CalculosEvent_RESULT_NEGATIVE";
public static const RESULT:String =
"CalculosEvent_RESULT";
public function CalculosEvent(type:String,
bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}
Estas constantes serão usadas no momento em que adicionarmos um listener
para elas. Como abordamos no tópico anterior, o evento é uma classe e
podemos adicionar propriedades a ele. Suponha então que queremos inserir
os valores do resultado, lembrando que estamos somando dois números.
Então podemos alterar o código do evento para:
package events
{
import flash.events.Event;
public class CalculosEvent extends Event
{
public static const RESULT_OVER100:String =
"CalculosEvent_RESULT_OVER100";
public static const RESULT_NEGATIVE:String =
"CalculosEvent_RESULT_NEGATIVE";
public static const RESULT:String =
"CalculosEvent_RESULT";
public var valor1:int;
public var valor2:int;
public function CalculosEvent(type:String,
valor1:int, valor2:int, bubbles:Boolean=false,
cancelable:Boolean=false)
{
this.valor1 = valor1;
this.valor2 = valor2;
super(type, bubbles, cancelable);
}
}
}
Criamos duas variáveis, que serão preenchidas no construtor do evento.
Agora que a classe relativa ao evento está pronta, podemos criar a classe que
fará o cálculo. Veja:
package matematica
{
import events.CalculosEvent;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
public class Calculos extends EventDispatcher
{
public function
Calculos(target:IEventDispatcher=null)
{
super(target);
}
public function Sum(valor1:int,valor2:int):int
{
var sum:int = valor1+valor2;
if (sum > 100)
dispatchEvent(new
CalculosEvent(CalculosEvent.RESULT_OVER100,valor1,valor2))
;
if (sum < 0)
dispatchEvent(new
CalculosEvent(CalculosEvent.RESULT_NEGATIVE,valor1,valor2)
);
dispatchEvent(new
CalculosEvent(CalculosEvent.RESULT,valor1,valor2));
return sum;
}
}
}
Nesta classe, usamos o método dispatchEvent para disparar um evento. Este
método está disponível porque a classe Calculos herda de EventDispatcher.
Quando usamos o dispatchEvent, criamos o evento CalculosEvent repassando
as constantes de acordo com a situação desejada. Veja que repassamos a
constante novamente, e os valores da soma.
Finalmente, para finalizarmos a aplicação, editamos a classe HelloWorld
instanciando a classe de Calculos e adicionando os listeners necessários,
veja:
package
{
import events.CalculosEvent;
import flash.display.Sprite;
import flash.events.Event;
import matematica.Calculos;
public class HelloWorld extends Sprite
{
public function HelloWorld()
{
var c:Calculos = new Calculos();
c.addEventListener(CalculosEvent.RESULT,OnResult);
c.addEventListener(CalculosEvent.RESULT_NEGATIVE,
OnResultNegative);
c.addEventListener(CalculosEvent.RESULT_OVER100,
OnResultOver100);
c.Sum(10,20);
c.Sum(90,20);
c.Sum(10,-20);
c.Sum(0,10);
}
private function OnResult(e:CalculosEvent):void
{
trace(" Result: ",e.valor1,e.valor2);
}
private function
OnResultNegative(e:CalculosEvent):void
{
trace(" Negative Result:
",e.valor1,e.valor2);
}
private function
OnResultOver100(e:CalculosEvent):void
{
trace(" Over 100 Result:
",e.valor1,e.valor2);
}
}
}
Resultado:
Result: 10 20
Over 100 Result: 90 20
Result: 90 20
Negative Result: 10 -20
Result: 10 -20
Result: 0 10
Agora você pode compreender melhor como todo o sistema baseado em
eventos funciona. Após criar o evento e a classe que dispara ele, podemos
usar o método addEvenetListener para capturar o evento disparado e
executar um método ou função anônima.
Talvez esse exemplo seja simples demais para que possamos entender o
poder da programação orientada a eventos, mas imagine que estamos criando
uma calculadora e que queremos deixar o valor em vermelho caso ele seja
negativo. O primeiro passo foi dado, pois quando a classe dispara o evento do
valor negativo, podemos facilmente alterar o status visual da calculadora.
No framework Flex, e em seus componentes, você verá o uso extensivo de
eventos e acabará se acostumando com eles, com a facilidade que eles
proporcionam e com o ganho de produtividade. Inclusive todos os
frameworks para Flex como o swiz são baseado em eventos.
Integração com o Flex
Durante toda esta obra estamos comentando sobre o Flex e o Flash Builder.
No início instalamos o Flash Builder e a partir dele criamos todos os nossos
exemplos. Agora vamos ampliar nosso conhecimento sobre a ferramenta
Flash Builder, mas sempre voltado ao Action Script.
Criando o projeto Flex
Com o Flash Builder aberto, acesse o menu File, New, Flex Project. Para o
campo Project name, escolha HelloFlex e em seguida clique no botão Finish.
Este assistente possui diversas configurações, mas que não são pertinentes
neste momento.
Ao criar o projeto, podemos perceber que o arquivo HelloFlex.mxml é aberto,
que com a linguagem MXML da aplicação ativada. Veja que até este
momento não temos nenhum código Action Script 3.0, apenas temos o
código MXML. Mas o Action Script 3.0 estará presente em quase tudo que
você fizer.
Vamos a um pequeno teste. Na tag <s:Application ...> existe um evento
chamado creatonComplete. Adicione-o dentro da tag e use a
complementação de código para gerar o handler do evento. Veja:
Ao gerar o handler, temos o seguinte código:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600"
creationComplete="OnComplete(event)"
>
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
protected function OnComplete(event:FlexEvent):void
{
// TODO Auto-generated method stub
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements
(e.g., services, value objects) here -->
</fx:Declarations>
</s:Application>
Como podemos ver, ao definir um handler para o evento creationComplete,
caímos na linguagem Action Script 3.0, onde podemos usar tudo que
aprendemos até agora.
Importando bibliotecas
O Flex possui um conjunto muito bom de bibliotecas que podem ser
incorporadas nos projetos. A maioria delas apresenta um arquivo com a
extensão swc, no qual podemos baixar e usar.
Vamos incorporar em nosso projeto a biblioteca flexlib, que já possui uma
versão para o Flex 4. Inicialmente acesse o site oficial da biblioteca:
http://code.google.com/p/flexlib/
Na lado direito, em Featured Downloads, clique no link “flexlib – 2.5 –
flex4.zip”. Baixe o arquivo e descompacte-o. Ao abrir o conteúdo do zip,
temos os seguintes arquivos/pastas:
Neste momento precisamos do arquivo swc, que é justamente toda a
biblioteca flexlib compilada e pronta para uso. Voltando ao projeto
HelloFlex, no Flash Builder, vamos incorporar esta biblioteca para o nosso
projeto. Para isso, acesse as propriedades do projeto HelloFlex, clicando com
botão direito do mouse sobre o projeto e escolhendo Properties:
Quando a janela de propriedades surgir, navegue até o item Flex Build Path e
na aba Library Path,clique no botão “Add SWC”:
Na janela que abre, clique no botão Browse, e selecione o arquivo
flexlib.swc.Clique em Ok para finalizar e repare que a biblioteca foi inserida
com sucesso:
Clique em OK para fechar a tela de propriedades do projeto HelloFlex, e
acesse o arquivo HelloFlex.mxml. Dentro da tag fx:Script, vamos fazer um
pequeno teste, verificando se a biblioteca flexlib está disponível para uso.
Para isso use o comando import, dentro da tag <fx:Script, faça o seguinte:
Veja que ao usar o import, podemos importar a biblioteca flexlib. Também
pode-se acessar o modo design e verificar os componentes que foram
adicionados na aba Components.
Criando sua própria biblioteca Action Script
É muito recomendável que você crie uma biblioteca Action Script
(chamaremos de lib) para os seus projetos, observando algumas dicas:
Crie uma biblioteca global, que poderá ser adicionada a qualquer
projeto Flex existente.
Crie uma biblioteca relacionada ao projeto em si. Nessa biblioteca
você coloca as classes Action Script que o projeto usa. No capítulo
anterior criamos diversas classes, relacionadas a pessoas, taxas,
veículos, matemática etc. Estas classes poderiam estar nessa
biblioteca.
A separação do projeto em “camadas” traz benefícios como uma melhor
separação do código, possibilitando que equipes possam trabalhar com mais
eficiência.
Para criar uma biblioteca, navegue até File, New, Flex Library Project.
Vamos criar o projeto chamado gLib, que seria o projeto global a qualquer
outro projeto Flex:
Após criar o projeto, vamos criar uma classe qualquer, dentro de um package
apropriado. Por exemplo, vamos criar uma classe chamada Remote em um
pacote chamado connection, que estende de RemoteObject, que é uma classe
para conexões AMF do Flex com o servidor:
Ao criarmos esta classe, temos o seguinte código:
package connection
{
import mx.rpc.remoting.mxml.RemoteObject;
public class Remote extends RemoteObject
{
public function Remote(destination:String=null)
{
super(destination);
}
}
}
Adicionando a biblioteca no projeto Flex
Agora que criamos a biblioteca glib, podemos facilmente adicioná-la ao
projeto HelloFlex. Acesse as propriedades do projeto HelloFlex e navegue até
Flex Build Path. Na aba Library Path, clique no botão Add Project e adicione
o projeto gLib.
É importante que você adicione o projeto e não o arquivo SWC. Isso
acontece porque ao adicionar como projeto as atualizações no projeto gLib
serão automaticamente refletidas no projeto principal.
O flexlib adicionamos como SWC porque ele está pronto para uso, e não
sofrerá modificações.
Como trabalhar com Action Script e MXML
Se você conhece HTML e JavaScript, possivelmente enxergou uma relação
entre o HTML e o MXML e o Action Script e JavaScript. Isso é verdade,
enquanto o MXML desenha a aplicação, o Action Script dá a lógica
necessária para ela.
Com isso temos um pequeno problema de organização. Como misturar esses
dois códigos sem prejudicar o entendimento do mesmo?
Utilizando arquivos Action Script externos
Algumas linguagens, como o .Net, tratam esta separação através de dois
arquivos separados, um contendo HTML e outro contendo código. Essa é
uma boa prática e podemos aplicá-la da seguinte forma:
No projeto HelloFlex, adicione um arquivo Action Script: File, New,
ActionScript File. Crie o arquivo HelloFlex.as. Este nome é o mesmo do
arquivo mxml, e de propósito, para que possamos saber que aquele arquivo
contém código relativo ao HelloFlex.mxml.
Até este momento temos a seguinte configuração do projeto:
O que iremos fazer agora é adicionar o arquivo HelloFlex.as no
HelloFlex.mxml, da seguinte forma:
Ou seja, através da propriedade source da tag <fx:Script/> conseguimos
adicionar um arquivo Action Script que possui somente código. Um pequeno
problema nesse aspecto é que quando usamos o Flash Builder para gerar os
nossos Handlers, o código será criado ainda no arquivo MXML, e você terá
que movê-lo manualmente para o arquivo Action Script.
Embutindo Action Script nos componentes
Outra forma de inserir Action Script no MXML é embuti-lo diretamente no
código. Pode parecer “sujo” a princípio, mas é uma solução que
particularmente gosto bastante. Vamos um exemplo simples, relacionado ao
evento de click do botão:
<s:Button label="Click-me">
<s:click>
<![CDATA[
Alert.show("Hello Flex");
]]>
</s:click>
</s:Button>
Este formato é o ideal para pequenos códigos relativos a eventos de
componentes. Se for necessário escrever muito código, então o melhor é criar
uma função separada (handler).
Diretamente na propriedade de um componente
Esta opção deve ser usada apenas para atribuições rápidas de uma variável.
No exemplo a seguir, criamos uma variável chamada estadosSudeste. Depois,
embutimos na propriedade dataprovider do ComboBox a variável que possui
os dados, veja:
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
protected var estadosSudeste:ArrayCollection = new
ArrayCollection(["MG","SP","ES","RJ"]);
]]>
</fx:Script>
<s:ComboBox dataProvider="{estadosSudeste}"
enabled="{estadosSudeste.length==4}"/>
Também inserimos outro código Action Script na propriedade enabled.
Como esta propriedade é booleana, podemos retornar para ela true ou false, e
isso é feito através da condição estadosSudeste.length==4.
Utilizando <fx:Script>
Esta é a forma mais comum, inclusive a usada pelo Flash Builder na geração
de código. Recomenda-se ter apenas um fx:script com código, e que ele
esteja no início do arquivo MXML.
Como estender componentes
Outro recurso muito utilizado no Action Script 3.0 é estender componentes
do Flex, e alterar de acordo com suas necessidades. Para fazer isso, basta
criar uma classe Action Script que herde de um componente Flex, como por
exemplo, o Button:
Crie a classe MyButton no projeto gLib
Código:
package components
{
import spark.components.Button;
public class MyButton extends Button
{
public function MyButton()
{
//TODO: implement function
super();
}
}
}
Dessa forma podemos implementar novas funcionalidades para qualquer tipo
de componente. No caso do Button, poderíamos alterar o label inicial, da
seguinte forma:
public function MyButton()
{
label = " Botão ";
super();
}
Como também poderíamos adicionar novos eventos e funcionalidades. Além
disso, pode-se adicionar novas interfaces para ao componente e com isso
garantir um melhor domínio em sua aplicação.
A possibilidade de expansão é tão grande que demandaria um novo livro,
para documentar e esclarecer melhor todos estes passos. A princípio, vamos
apenas observar que com o Action Script 3.0 é possível fazer muito mais do
que apenas telas de cadastro, bastando apenas conhecer bem a linguagem e
padrões de projeto. Os padrões ajudam a criar sistemas maiores, sem criar
código excessivamente ruim, prezando sempre por uma melhor manutenção
do código.
Aplicando padrões de projeto
Este capítulo aborda alguns padrões de projeto que são comuns em qualquer
linguagem de programação OO. É importante perceber que os padrões de
projeto são extremamente úteis em diversas situações, e que abordar todos
eles nesta obra daria uma nova obra. A idéia principal deste capítulo é
reforçar os conceitos de OO aprendidos com algo mais prático e não
enumerar todos os padrões existentes.
Iremos inicialmente criar um tipo de problema, propondo uma solução ruim.
Iremos dizer o porquê da solução ruim e depois propor uma solução baseada
em algum padrão de projetos.
Entendendo Padrões de Projeto
Antes de começar a mostrar os padrões de projeto, vamos tentar te convencer
a por que usar estes padrões. Muitas pessoas ainda não conseguem enxergar a
utilidade deles, e sofrem depois com manutenção em programas. Vamos
tentar reverter isso, mostrando este exemplo.
Vamos supor que estamos criando pizzas, dado um parâmetro qualquer:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value
objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayList;
private var pizzas:ArrayList = new
ArrayList(["Calabresa","Presunto","4 Queijos"]);
private var pizza:Pizza;
]]>
</fx:Script>
<s:ComboBox id="combo" dataProvider="{pizzas}">
<s:change>
<![CDATA[
if (combo.selectedItem=="Calabresa")
pizza = new Calabresa();
else if (combo.selectedItem=="Presunto")
pizza = new Presunto();
else if (combo.selectedItem=="4 Queijos")
pizza = new QuatroQueijos();
]]>
</s:change>
</s:ComboBox>
</s:Application>
Neste exemplo (Flex Project), criamos um comboBox com algumas pizzas.
Dependendo do que o usuário escolher (), criamos a pizza. Também temos
as classes que representam uma pizza:
package
{
public class Pizza
{
public function Pizza()
{
}
}
}
package
{
public class Calabresa extends Pizza
{
public function Calabresa()
{
trace("Pizza Calabresa");
}
}
}
package
{
public class Presunto extends Pizza
{
public function Presunto()
{
trace("Pizza Presunto");
}
}
}
package
{
public class QuatroQueijos extends Pizza
{
public function QuatroQueijos()
{
trace("Quatro Queijos");
}
}
}
Veja que este código está completamente correto. Que está compilando e
funcionando muito bem. O que precisamos entender é que padrões de
projeto foram criados para criamos programas dos quais podemos dar
manutenção. O código acima parece correto porque o programa está
pequeno, muito pequeno.
Imagine agora a seguinte situação: O seu programa está completo, você tem
1000 linhas de código, tudo funcionando. Você usou a abordagem acima, ou
seja, criou as pizzas no evento change do comboBox.
Agora veja esta nova situação:
Além do combobox, em uma segunda feira qualquer, o seu chefe pediu para
colocar uma caixa de texto onde o vendedor pode inserir um código e assim
criar a pizza. Nesse ponto, você está na pressão, tem que entregar
funcionando hoje, e faz o seguinte:
<s:TextInput id="caixaTexto" width="100" top="30">
<s:keyDown>
<![CDATA[
if (caixaTexto.text=="1")
pizza = new Calabresa();
else if (caixaTexto.text=="2")
pizza = new Presunto();
else if (caixaTexto.text=="3")
pizza = new QuatroQueijos();
]]>
</s:keyDown>
</s:TextInput>
<s:ComboBox id="combo" dataProvider="{pizzas}">
<s:change>
<![CDATA[
if (combo.selectedItem=="Calabresa")
pizza = new Calabresa();
else if (combo.selectedItem=="Presunto")
pizza = new Presunto();
else if (combo.selectedItem=="4 Queijos")
pizza = new QuatroQueijos();
]]>
</s:change>
</s:ComboBox>
Aqui começam os problemas. O primeiro ato desesperado de um
programador é copiar e colar o código, e isso é a pior coisa que ele pode fazer
na vida. Porque? Por que quando formos adicionar uma nova pizza, teremos
que alterar em dois lugares diferentes. Não existe nada pior que isso. No
código acima pode até parecer fácil, mas quando temos um código muito
extenso, com umas 1000 linhas separando cada função, certamente você vai
alterar em um lugar e não vai alterar em outro. Imagine quando existem
códigos clonados em dois arquivos diferentes. Resultado: seu chefe te liga
dizendo que o combobox está criando a nova pizza, mas a caixa de texto não.
Melhorando um pouco esta solução, e acredito que você vai querer melhorá-
la, vamos criar um método que contém todos esses IFs. Veja:
<fx:Script>
<![CDATA[
import mx.collections.ArrayList;
private var pizzas:ArrayList = new
ArrayList(["Calabresa","Presunto","4 Queijos"]);
private var pizza:Pizza;
private function CreatePizza(tipo:String):void
{
if (tipo=="Calabresa" || tipo == "1")
pizza = new Calabresa();
else if (tipo=="Presunto" || tipo =="2")
pizza = new Presunto();
else if (tipo=="4 Queijos" || tipo == "3")
pizza = new QuatroQueijos();
}
]]>
</fx:Script>
<s:TextInput id="caixaTexto" width="100" top="30">
<s:keyDown>
<![CDATA[
CreatePizza(caixaTexto.text);
]]>
</s:keyDown>
</s:TextInput>
<s:ComboBox id="combo" dataProvider="{pizzas}">
<s:change>
<![CDATA[
CreatePizza(combo.selectedItem);
]]>
</s:change>
</s:ComboBox>
Agora sim, você centralizou tudo em uma função. Então você acredita que
melhorou muito o código e vai pra casa feliz! Dois meses depois, seu chefe
chega em uma manhã de segunda feira e diz: “Sabe aquele programa de
pizzas? Agora coloca pra mim a pizza de chocolate”. Aqui surge outro grande
problema e é justamente aqui que entra os padrões de projeto. Nós somos
seres humanos, e não máquinas. A primeira coisa que irá pensar enquanto
carrega o programa de pizzas é: “onde mesmo que eu coloquei a função que
cria pizzas????”. E vai levar um bom tempo para achá-la, e adicionar a nova
pizza. Ok, você levou um tempo para adicionar a pizza no método
CreatePizza. Agora você precisa criar a classe Chocolate, que herda de pizza.
“Nossa!! - você diz – Isso foi muito fácil. Eu fui no package pizzas e só criei
a classe!!”.
Viu a diferença entre criar uma nova classe e alterar uma classe existente?
Muitos padrões de projeto são baseados nisso, em criar novas coisas ao invés
de alterar coisas existentes.
Aliás criar ao invés de alterar é uma boa metodologia OO chamada: Open Closed Principle. Se você está gostando de Design Patterns poder ler este link após terminar de ler o livro: http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
Agora vamos dar um jeito no programa de Pizzas!!
Factory
A palavra Factory sozinha não quer dizer nenhum padrão de projetos.
Existem dois principais padrões (Abstract Factory e Factory Method) que
usam este conceito e são os padrões mais utilizados. Antes de abordá-los
temos que compreender o que é um Factory. Em inglês, factory quer dizer
fábrica e conceitualmente falando, uma fábrica cria alguma coisa.
Então o padrão que tem o Factory no nome, é responsável em criar algo. No
programa de pizzas podemos usar um Factory para criar as pizzas, veja:
<fx:Script>
<![CDATA[
import mx.collections.ArrayList;
private var pizzas:ArrayList = new
ArrayList(["Calabresa","Presunto","4 Queijos"]);
private var pizza:Pizza;
private function CreatePizza(tipo:String):void
{
pizza = PizzaFactory.CreatePizza(tipo);
}
]]>
E a classe PizzaFactory:
package
{
public class PizzaFactory
{
public function PizzaFactory()
{
}
public static function CreatePizza(tipo:String):Pizza
{
if (tipo=="Calabresa" || tipo == "1")
return new Calabresa();
else if (tipo=="Presunto" || tipo =="2")
return new Presunto();
else if (tipo=="4 Queijos" || tipo == "3")
return new QuatroQueijos();
return null;
}
}
Ok, agora você deve estar imaginando: “Ah, você somente „empurrou‟ um
método para uma classe...”. A minha resposta é: sim, eu fiz isso. Mas por
quê? Por que estamos criando um padrão (Lembre-se, estamos estudando
padrões de projeto), e a partir de agora, em qualquer parte do programa,
sempre que eu quiser criar objetos dado alguma característica, vamos usar
uma classe Factory.
Factory Method
Ah, e parabéns, você acabou de aprender seu primeiro padrão de projetos,
chamado Factory method. Ele é usado sempre que criamos classes mas não
sabemos ao certo qual classe criar, porque existem parâmetros a serem
passados.
Agora, quando o seu chefe pedir para criar uma nova pizza, você já sabe:
tenho que encontrar a classe PizzaFactory.
Voltando ao projeto das Pizzas, nós ainda temos um problema. Inicialmente
estamos preenchendo o ComboBox manualmente. Ou seja, para adicionar
uma nova pizza nós temos que abrir o arquivo mxml e preencher mais um
item. Certamente você não lembrará disso, quando for adicionar uma nova
Pizza. Então para resolver este problema, podemos melhorar o nosso Factory:
package
{
import flash.utils.describeType;
import flash.utils.getDefinitionByName;
import mx.collections.ArrayList;
import mx.core.ClassFactory;
public class PizzaFactory
{
public function PizzaFactory()
{
}
private static function configurePizzas():ArrayList
{
var pizzas:ArrayList = new ArrayList();
pizzas.addItem(new Calabresa());
pizzas.addItem(new Presunto());
pizzas.addItem(new QuatroQueijos());
return pizzas;
}
public static function GetPizzasName():ArrayList
{
var pizzasName:ArrayList = new ArrayList();
var pizzas:ArrayList =
PizzaFactory.configurePizzas();
for each (var pizza:Pizza in pizzas.source)
{
pizzasName.addItem(pizza.Name);
}
return pizzasName;
}
public static function CreatePizza(tipo:String):Pizza
{
var pizzas:ArrayList =
PizzaFactory.configurePizzas();
var newPizza:Pizza;
for each (var pizza:Pizza in pizzas.source)
{
if (tipo == pizza.Name || tipo ==
pizza.Codigo)
{
var reflection : XML =
describeType(pizza);
var classToInstantiate : Class =
getDefinitionByName(reflection.@name) as Class;
var myClassFactory : ClassFactory =
new ClassFactory(classToInstantiate);
var myObjectInstance :Pizza =
myClassFactory.newInstance();
return myObjectInstance;
}
}
return null;
}
}
}
Agora, a nossa PizzaFactory está completa. O método configurePizzas, em
, é responsável em configurar quais as Pizzas estão disponíveis no sistema.
Veja que esse método é estático, para que possamos chamar o método com
mais facilidade do mxml principal (ou de outro lugar).
O uso de métodos estáticos é comum nesse padrão, a perda de performance é
muito pequena em relação a um método normal e o ganho de produtividade
compensa a perda de processamento.
Veja que, em , estamos adicionando uma instância da classe Calabresa a
um array de pizzas. Em , criamos o método GetPizzasName, que é
responsável em obter as pizzas configuradas e retornar um array contendo o
nome de cada uma das pizzas.
O método CreatePizza não usa mais if/else if para retornar a instância correta
da classe. Agora usamos o método configurePizzas para obter as pizzas que
estão cadastradas, e em fazemos um for para verificarmos, uma a uma, se
o nome ou código fornecido são válidos. Quando acharmos a pizza escolhida,
iniciamos uma seqüência de códigos em na qual chamamos de reflection,
muito usada em linguagens como Java e C#, com o propósito de criar a
instância de uma classe de forma dinâmica. Com isso não precisamos fazer,
por exemplo, o new Calabresa(), pois é feito automaticamente.
Dica: Gostou de reflection? Você pode ver mais neste link: http://flexiblegorilla.com/wordpress/?p=140
Em , retornamos a instância do objeto, no qual podemos usá-lo da forma
que quisermos.
O código principal ficou extremamente simples, veja:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services,
value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
private var pizza:Pizza;
]]>
</fx:Script>
<s:TextInput id="caixaTexto" width="100" top="30">
<s:keyDown>
<![CDATA[
PizzaFactory.CreatePizza(caixaTexto.text);
]]>
</s:keyDown>
</s:TextInput>
<s:ComboBox id="combo"
dataProvider="{PizzaFactory.GetPizzasName()}">
<s:change>
<![CDATA[
PizzaFactory.CreatePizza(combo.selectedItem);
]]>
</s:change>
</s:ComboBox>
</s:Application>
Repare que o combo usa o Factory para obter a lista de pizzas e usa o
Factory para obter uma instância da pizza selecionada.
Agora, quando o seu chefe pedir para adicionar mais uma pizza no sistema,
você irá pensar: “Eu usei neste sistema o padrão Factory, que possui fábricas
para criar e manipular objetos. Então eu tenho que criar uma nova classe que
herda de pizza e depois acessar método configure da PizzaFactory para
adicionar essa nova classe na minha lista de pizzas”.
Abstract Factory
O padrão abstract factory é uma extensão ao Factory Method. O Abstract
Factory encapsula vários factories que possuem alguma relação. Agora que
você já conhece o padrão factory, pode, por exemplo, criar um conjunto de
classes relativo ao uso de ingredientes.
Mas antes de começarmos a codificar, vamos esclarecer um ponto importante
sobre OO e sobre o Action Script 3.0. No mundo OO existe um tipo de classe
chamado “classe abstrata”. Até o padrão Abstact Factory é relacionado a
essas classes.
Por definição, uma classe abstrata é uma classe que não pode ser instanciada,
e cujo os seus métodos podem ser reutilizados pelas classes filhas. Quando
discutíamos sobre OO, ensinamos a sempre comparar nossas classes com o
mundo real. Fazendo essa comparação, descobrimos que ninguém quer comer
uma pizza vazia, com apenas a massa e sem recheio. Com isso deduzimos
que não poderíamos, a princípio, criar uma classe Pizza (new Pizza() ). Isso
sugere fortemente que a classe Pizza é abstrata.
Mas então encontramos um problema. No Action Script 3.0, não existe o
conceito de classe abstrata, e para termos uma classe com um comportamento
um pouco semelhante à classe abstrata, precisamos de certa forma programar
mais um pouco. Este tipo de programação não é o foco desta obra, pois
estamos apenas mostrando algumas particularidades dos padrões de projeto
na linguagem Action Script 3.0.
Singleton
Este padrão é bastante conhecido pelos usuários Java e C#. Em teoria, este
padrão diz que uma classe deve somente possuir uma instância durante todo o
sistema. Você pode pensar: “isso é uma variável global!!”. Os conceitos
realmente são muito semelhantes, mas o uso de Singleton é mil vezes melhor
do que usar variáveis globais.
Aliás, o Flex possui dezenas de classes Singleton, e talvez você já tenha
utilizados sem saber. Na maioria dos casos, as classes Singleton terminam
com a palavra Manager – uma convenção da Adobe. Por exemplo, a classe
PopUpManager que é responsável em adicionar janelas PopUp no Flex é uma
classe singleton.
Criar uma classe singleton com Java ou C# é eventualmente simples. Existe
uma “receita de bolo” para isso, e iremos usá-la aqui. Para criamos uma
classe Singleton, podemos usar o seguinte código:
class MySingleton
{
private static var _instance: MySingleton;
public function MySingleton ( ) { }
public static function getInstance( ): MySingleton
{
if (_instance == null) {
MySingleton. _instance = new MySingleton ( );
}
return MySingleton._instance;
}
}
Uma das poucas diferenças desta receita é o uso do public no método
construtor da classe. Teoricamente uma classe Singleton não pode ser
instanciada, então o método construtor deveria ser private, mas o Action
Script 3.0 não permite isso.
Vamos a um exemplo prático. Suponha que no seu sistema você queria
controlar o usuário logado nele. Esse usuário é representado por uma classe
que contém os seus dados (Usuario.as) e outra classe que contém o padrão
Singleton, chamada de UsuarioManager (seguindo o padrão do Flex).
package
{
public class Usuario
{
public var Nome:String;
public var Email:String;
public function Usuario()
{
}
}
}
e:
package
{
public class UsuarioManager
{
private static var _instance:UsuarioManager;
public var UsuarioLogado:Usuario;
public function UsuarioManager()
{
}
public static function
getInstance():UsuarioManager
{
if (_instance == null)
_instance = new UsuarioManager();
return _instance;
}
}
}
Analisando a classe UsuarioManager, percebemos que a sua estrutura é
parecida com a receita de bolo do padrão Singleton.Temos um método
chamado getInstance() que retorna a instância do UsuárioManager. Na
primeira vez que getInstace() é chamado, a variável _instance é instanciada e
a partir deste momento, ela é sempre usada independente de onde é chamada.
No exemplo a seguir, criamos uma analogia a uma tela de login. Suponha que
assim que o usuário efetuar o login no sistema, preenchemos a variável
UsuárioLogado do UsuarioManager.
Na aplicação principal temos:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import mx.managers.PopUpManager;
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value
objects) here -->
</fx:Declarations>
<s:creationComplete>
<![CDATA[
//Faz de conta que o usuário acabou de logar....
var u:Usuario = new Usuario();
u.Nome = "Daniel";
u.Email = "[email protected]";
UsuarioManager.getInstance().UsuarioLogado = u;
//Adicionamos um Popup !!
var popup:MyPopUp = new MyPopUp();
PopUpManager.addPopUp(popup,this);
]]>
</s:creationComplete>
</s:Application>
Veja que em , no evento creationComplete da aplicação, criamos um objeto
da classe Uauário, e atribuímos este objeto a propriedade UsuarioLogado do
UsuarioManager (). Em , criamos um popup, que abre um outro
componente mxml. Neste componente, usamos o padrão Singleton para obter
a classe UsuarioManager e usarmos algumas propriedades da classe:
<?xml version="1.0" encoding="utf-8"?>
<s:Panel xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="400" height="300">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services,
value objects) here -->
</fx:Declarations>
<s:creationComplete>
<![CDATA[
this.title = "Olá " +
UsuarioManager.getInstance().UsuarioLogado.Nome;
]]>
</s:creationComplete>
</s:Panel>
Retirando o getInstance()
Na verdade não podemos simplesmente retirar a chama ao método
getInstance(), mas podemos escondê-la do usuário final e implementá-la
dentro da classe UsuarioManager, conforme a refatoração a seguir:
package
{
public class UsuarioManager
{
private static var _instance:UsuarioManager;
public var UsuarioLogado:Usuario;
public function UsuarioManager()
{
}
protected static function
getInstance():UsuarioManager
{
if (_instance == null)
_instance = new UsuarioManager();
return _instance;
}
public static function getUsurario():Usuario
{
return getInstance().UsuarioLogado;
}
public static function
setUsuarior(_usuario:Usuario):void
{
getInstance().UsuarioLogado = _usuario;
}
public static function isLogged():Boolean
{
return getInstance().UsuarioLogado != null;
}
}
}
Veja que o método getInstance() continua o mesmo, exceto pela mudança de
public para protected. Assim, o método não é visto por quem estiver usando a
classe UsuarioManager. Os outros métodos, como o getUsuario(), são
públicos e usam o getInstance() internamente ().
Assim, os métodos surgem para o usuário na complementação de código no
próprio FlashBuilder, como na figura a seguir:
O uso do Singleton é um grande aliado na melhoria de código. A dica é
NUNCA usar parentAplication em sua aplicação. Se você não sabe o que é
parentAplication, é melhor não saber mesmo.
Observer
O padrão Observer é bastante usado no framework Flex, em diversas partes
do código, como nos eventos e databinds existentes. O próprio databind de
um componente faz com que o uso do padrão Observer seja reduzido, mais
ainda existe muitas aplicações para ele.
Este padrão é formado de uma interface chamada Observer, e de uma outra
classe que se registra no observador. O padrão possui um método qualquer,
que através de um for each dispara alguma coisa na classe registradora dado
algum acontecimento.
Vamos a um exemplo prático para podermos entender melhor o processo.
Suponha que em uma aplicação capaz de pegar botões na tela e embaralhar
eles (alterar o x e y). Existem dois jeitos de fazer isso. O primeiro, mostrado a
seguir, não usamos nenhum padrão e temos que fazer quase tudo na mão,
veja:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import spark.components.Button;
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services,
value objects) here -->
</fx:Declarations>
<s:creationComplete>
<![CDATA[
for (var i:int = 0;i<10;i++)
{
var b:Button = new Button();
b.label = i.toString();
b.x = Math.random() * 300;
b.y = Math.random() * 300;
this.addElement(b);
}
]]>
</s:creationComplete>
<s:Button label="De novo!">
<s:click>
<![CDATA[
for (var i:int = 0;i<10;i++)
{
var b:Button = getElementAt(i) as
Button;
b.x = Math.random() * 300;
b.y = Math.random() * 300;
}
]]>
</s:click>
</s:Button>
</s:Application>
Neste código, criamos em um for um conjunto de botões com x e y
randômicos. Criamos também o botão “De novo”, que repete o código para
alterar X e Y. Nós podemos melhorar muito esse código, mesmo sem usar
padrões. Por exemplo, poderíamos criar uma função que altera os valores
randomicamente e poderíamos criar uma variável que contém o número de
botões que serão inseridos na tela.
Estas soluções melhoram o código, mas esbarram no problema da
manutenção. Suponha que daqui a dois meses você precise alterar a
quantidade de botões na tela. Se for um programa muito grande, você vai
demorar um pouco para encontrar a variável que diz essa quantidade.
Suponha que agora o programa é em 3D e existe a variável Z. Onde mesmo
que está a função que embaralha as coordenadas? Tudo isso causa transtornos
na manutenção de código e podemos usar padrões para resolver o problema.
O padrão de projetos observer faz com que criemos um observador e
colocamos algumas classes registradas nesse observador. Então o observador
tem a habilidade de notificar essas classes quando quiser.
Vamos inicialmente criar duas interfaces:
package
{
public interface IObserver
{
function notify():void;
}
}
e:
package
{
public interface IRegister
{
function add(o:IObserver):void;
function notifyAll():void;
}
}
Estas duas interfaces são o esqueleto básico do padrão observer. Dessa
forma, podemos usá-la em qualquer lugar dentro da aplicação, sempre
aproveitando o “esqueleto” da interface (você pode trocar a palavra esqueleto
por contrato, caso fique mais fácil de entender).
Agora vamos criar as classes que implementam estas interfaces. Vamos
chamá-las de ButtonRegister e ButtonObserver. Use o FlashBuilder para criar
a classe e certifique-se de adicionar a interface correta no assistente.
package
{
public class ButtonObserver implements IObserver
{
public function
ButtonObserver(container:SkinnableContainer, label:String)
{
}
public function notify():void
{
}
}
}
Inicialmente ButtonObserver apenas traz os métodos que devemos
implementar. O mesmo acontece com o ButtonRegister:
package
{
public class ButtonRegister implements IRegister
{
public function ButtonRegister()
{
}
public function add(o:IObserver):void
{
}
public function notifyAll():void
{
}
}
}
Agora vamos implementar cada classe. O ButtonObserver precisa adicionar
um botão na tela e também precisa do código para embaralhar as
coordenadas:
package
{
import spark.components.Button;
import spark.components.SkinnableContainer;
public class ButtonObserver implements IObserver
{
protected var _Button:Button;
public function ButtonObserver(
container:SkinnableContainer, label:String)
{
_Button = new Button();
_Button.label = label;
container.addElement(_Button);
notify();
}
public function notify():void
{
this._Button.x = Math.random()*300;
this._Button.y = Math.random()*300;
}
}
}
Veja que criamos um Spark Button e adicionamos ele ao container. Em
notify, alteramos a propriedade x e y do botão.
Veja que agora o ButtonObserver altera as propriedades do botão. No nosso
primeiro exemplo, quem fazia isso era a aplicação principal. E isso é muito
ruim, muito ruim mesmo, porque quando formos dar manutenção, ficaríamos
um bom tempo tentando achar a função que muda o x e y do botão. Quando
temos o padrão observer, sabemos que somente o observer pode alterar as
propriedades relacionadas ao que se deseja fazer, então é mais fácil aplicar
uma manutenção no futuro.
Agora vamos analisar a implementação da classe ButtonRegister:
package
{
import mx.collections.ArrayList;
public class ButtonRegister implements IRegister
{
protected var buttons:ArrayList;
public function ButtonRegister()
{
buttons = new ArrayList();
}
public function add(o:IObserver):void
{
buttons.addItem(o);
}
public function notifyAll():void
{
for each (var btn:IObserver in
buttons.source)
{
btn.notify();
}
}
}
}
Esta classe possui um Array de IObservers, e é responsável em manter os
observers registrados, e em notificar cada um deles quando preciso, através
do método notifyAll.
Agora com a nossa arquitetura pronta, vamos ao código final:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import spark.components.Button;
protected var buttonRegister:ButtonRegister;
]]>
</fx:Script>
<s:creationComplete>
<![CDATA[
buttonRegister = new ButtonRegister();
buttonRegister.add(new ButtonObserver(this,"1"));
buttonRegister.add(new ButtonObserver(this,"2"));
buttonRegister.add(new ButtonObserver(this,"3"));
buttonRegister.add(new ButtonObserver(this,"4"));
]]>
</s:creationComplete>
<s:Button label="De novo">
<s:click>
<![CDATA[
buttonRegister.notifyAll();
]]>
</s:click>
</s:Button>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services,
value objects) here -->
</fx:Declarations>
</s:Application>
No código principal, criamos um único ButtonRegister e adicionamos vários
Observers. O botão “De novo” possui agora uma única chamada, que é o
notifyAll do Register.
Este é apenas um exemplo de como podemos usar o padrão Observer para
realizarmos ações em objetos que implementam a interface IObserver.
Exemplo real com Observer
Agora vamos aplicar o padrão Observer em um exemplo Real! Suponha que
você tem um formulário, com muitos campos dos mais diversos tipos:
textbox, checkbox, combobox...
Então você precisa implementar um botão para limpar todos os campos do
formulário. No pior caso, esse botão chama um método no qual você limpa os
dados de cada um dos campos, um a um. Talvez essa seja a forma mais
comum usada...
Agora vamos implementar o padrão de projeto Observer nesse problema. Nós
teremos então as seguintes classes:
IObserver e IRegister: São as mesmas classes do exemplo anterior.
Essas classes precisam ser criadas apenas uma única vez. Dica:
Lembra da gLib? Essas classes podem ficar lá e um package
“pattern.observer”.
ClearObserver: É a implementação do observador, que irá “limpar” o
componente
ClearRegister: Contém a lista de objetos para serem “limpados”.
Vamos ao código:
package
{
import mx.core.UIComponent;
import spark.components.CheckBox;
import spark.components.ComboBox;
import spark.components.TextInput;
public class ClearObserver implements IObserver
{
protected var component:UIComponent;
public function ClearObserver
(component:UIComponent)
{
this.component = component;
}
public function notify():void
{
if (this.component is TextInput)
(this.component as TextInput).text =
"";
else if (this.component is CheckBox)
(this.component as CheckBox).selected
= false;
else if (this.component is ComboBox)
(this.component as
ComboBox).selectedIndex = -1;
/* e mais controles */
}
}
}
e:
package
{
import mx.collections.ArrayList;
public class ClearRegister implements IRegister
{
protected var components:ArrayList;
public function ClearRegister()
{
this.components = new ArrayList();
}
public function add(o:IObserver):void
{
this.components.addItem(o);
}
public function notifyAll():void
{
for each (var component:IObserver in
components.source)
component.notify();
}
}
}
Agora vamos a nossa implementação final, com o formulário:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
protected var clearRegister:ClearRegister;
]]>
</fx:Script>
<s:creationComplete>
<![CDATA[
clearRegister = new ClearRegister();
clearRegister.add(new ClearObserver(txt));
clearRegister.add(new ClearObserver(cmb));
clearRegister.add(new ClearObserver(check));
]]>
</s:creationComplete>
<s:TextInput x="52" y="30" id="txt"/>
<s:ComboBox x="52" y="60" id="cmb"/>
<s:CheckBox x="52" y="91" label="CheckBox" id="check"/>
<s:Button label="Limpar" x="110" y="129">
<s:click>
<![CDATA[
clearRegister.notifyAll();
]]>
</s:click>
</s:Button>
</s:Application>
Execute o programa e veja o comportamento de como o cadastro dos
observadores é executado no notifyAll(). Parece mágica, mas não é !! Desta
forma, você não precisará mais saber como “limpar” cada controle em um
formulário, você apenas precisará adicionar os componentes com o
observador correto, que neste caso é o ClearObserver. Nada impede de você
criar o ValidateObserver e o DefaultObserver, todos responsáveis em realizar
tarefas especificas, mas que estão encapsuladas em seus devidos lugares.
Para finalizarmos o padrão Observer, analise atentamente as classes
ButtonRegister e ClearRegister. Qual a diferença entre elas? Nenhuma,
apenas mudamos o nome da variável que segura a lista de observadores.
Isso significa que podemos ter um Registrador padrão, e quem determina a
operação a ser realizada é o observador. Com isso, teríamos três classes na
nossa biblioteca gLib: IObserver, IRegister e GenericRegister.
Combinando Padrões (Factory + Observer)
Chegamos ao último capítulo desta obra. Após vermos alguns padrões de
projeto, pudemos analisar como eles podem nos ajudar na manutenção de
código.
Lembre: os padrões de projeto podem nos consumir um pouco mais tempo
para desenvolvermos uma certa arquitetura. Você pode passar alguns minutos
ou horas pensando em qual padrão aplicar para aquele tipo de problema. Mas
assim que você cria e implementa o padrão, tudo fica muito mais fácil.
Para que possamos ter certeza que o padrão aplicado ao problema está bom,
basta olharmos para o resultado e analisarmos se estamos adicionando classes
no sistema ou se estamos alterando classes no sistema. Se a primeira
alternativa está ocorrendo, ou seja, você está adicionando classes e
alimentando arrays de configuração, estás no caminho certo.
Se para adicionar uma funcionalidade no sistema você precise aumentar um if
ou else if , então algo pode ser melhorado e você pode gastar um pouco mais
de tempo com isso.
Se para adicionar uma funcionalidade no sistema você precise alterar vários
arquivos, então .... para tudo! Você está caminhando para um caminho sem
volta, de muita manutenção e estresse!
Bom, após observar estes fatos, vamos novamente olhar um pedaço do nosso
código do padrão Observer:
public function notify():void
{
if (this.component is TextInput)
(this.component as TextInput).text =
"";
else if (this.component is CheckBox)
(this.component as CheckBox).selected
= false;
else if (this.component is ComboBox)
(this.component as
ComboBox).selectedIndex = -1;
/* e mais controles */
}
Este código pode ser melhorado e podemos transformar estes ifs em um
Factory, assim como aprendemos no capítulo sobre Factory Method.
Agora que estamos unindo dois padrões, é preciso cautela e temos que pensar
um pouco nas classes envolvidas. Como este é um dos exemplos mais
complexos do livro, vamos analisá-lo passo a passo. A seguir vamos ilustrar
as classes que compõem o nosso sistema:
ExemploObserver.mxml: É a aplicação principal do sistema. Nela temos
apenas que informar ao Registrador Genérico os observadores relativos a
limpar campos. O arquivo em si é bastante enxuto, veja:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
protected var clearRegister:GenericRegister;
]]>
</fx:Script>
<s:creationComplete>
<![CDATA[
clearRegister = new GenericRegister();
clearRegister.add(new ClearObserver(txt));
//clearRegister.add(new ClearObserver(cmb));
clearRegister.add(new ClearObserver(check));
]]>
</s:creationComplete>
<s:TextInput x="52" y="30" id="txt"/>
<s:ComboBox x="52" y="60" id="cmb"/>
<s:CheckBox x="52" y="91" label="CheckBox" id="check"/>
<s:Button label="Limpar" x="110" y="129">
<s:click>
<![CDATA[
clearRegister.notifyAll();
]]>
</s:click>
</s:Button>
</s:Application>
A primeira nova classe que vimos aqui é GenericRegister. Discutido no
capítulo anterior, descobrimos que a classe de registro poderia ser genérica, e
implementamos ela neste novo exemplo.
package
{
import mx.collections.ArrayList;
public class GenericRegister implements IRegister
{
protected var observers:ArrayList;
public function GenericRegister()
{
this.observers = new ArrayList();
}
public function add(o:IObserver):void
{
this.observers.addItem(o);
}
public function notifyAll():void
{
for each (var observer:IObserver in
this.observers.source)
observer.notify();
}
}
}
As classes IRegister e IObserver continuam as mesmas:
package
{
public interface IRegister
{
function add(o:IObserver):void;
function notifyAll():void;
}
}
e
package
{
public interface IObserver
{
function notify():void;
}
}
A classe ClearObserver possui novidades, veja:
package
{
import mx.core.UIComponent;
import spark.components.CheckBox;
import spark.components.ComboBox;
import spark.components.TextInput;
public class ClearObserver implements IObserver
{
protected var myComponent:IComponent;
public function
ClearObserver(component:UIComponent)
{
this.myComponent =
ComponentFactory.createComponent(component);
if (this.myComponent == null)
throw new Error("Não foi possível
encontrar o componente para executar a ação de Clear");
this.myComponent.component = component;
}
public function notify():void
{
this.myComponent.clear();
}
}
}
No método construtor ClearObserver, usamos um Factory para obtermos o
componente relativo ao componente repassado pelo mxml principal. Aqui
entra a união entre o padrão Observer com o padrão Factory.
Relembrando, o Factory é uma fábrica de objetos, que retorna um objeto
específico dado algum parâmetro. No nosso caso, o parâmetro é o próprio
componente que está no arquivo mxml. Baseado neste parâmetro, retornamos
uma classe que possui funcionalidades ligada àquele componente.
Vamos analisar então o Factory:
package
{
import flash.utils.describeType;
import flash.utils.getDefinitionByName;
import mx.collections.ArrayList;
import mx.core.ClassFactory;
import mx.core.UIComponent;
import myComponents.CheckBox;
import myComponents.TextInput;
public class ComponentFactory
{
public function ComponentFactory()
{
}
public static function getComponents():ArrayList
{
var componentsList:ArrayList = new
ArrayList();
componentsList.addItem(new
myComponents.TextInput());
componentsList.addItem(new
myComponents.CheckBox());
return componentsList;
}
public static function
createComponent(component:UIComponent):IComponent
{
var reflection:XML =
describeType(component);
for each (var myComponent:IComponent in
getComponents().source)
{
if (myComponent.getParentName() ==
reflection.@name)
{
var reflectionMyComponent:XML =
describeType(myComponent);
var classToInstantiate : Class
= getDefinitionByName(reflectionMyComponent.@name) as
Class;
var myClassFactory :
ClassFactory = new ClassFactory(classToInstantiate);
var myObjectInstance :
IComponent = myClassFactory.newInstance();
return myObjectInstance;
}
}
return null;
}
}
}
Este factory possui dois métodos estáticos. O primeiro deles (), chamado
de getComponents possui o mesmo comportamento do método configure,
visto no capítulo sobre Factory. O objetivo de getComponents é retornar uma
lista de componentes que são da interface IComponent, da qual podemos
realizar operações, como “limpar” o componente.
Vamos analisar a interface IComponent:
package
{
import mx.core.UIComponent;
public interface IComponent
{
function getParentName():String;
function clear():void;
function get component():UIComponent;
function set
component(component:UIComponent):void;
}
}
A interface diz que precisamos implementar alguns métodos, como
getParentName() e clear(), além de fornecermos uma forma de acessar o
componente da tela. Lembre: Existe o componente da tela e existe a classe
que manipula esse componente, que implementa IComponent.
Por exemplo, criamos o package MyComponents e nele criamos a classe
TextInput. A principal funcionalidade da classe MyComponents.TextInput é
realizar operações no componente spark.components.TextInput. A classe
MyComponents.TextInput é exibida a seguir:
package myComponents
{
import mx.core.UIComponent;
import spark.components.TextInput;
public class TextInput implements IComponent
{
protected var _component:UIComponent;
public function TextInput()
{
}
public function getParentName():String
{
return "spark.components::TextInput";
}
public function clear():void
{
(_component as
spark.components.TextInput).text = "";
}
public function get component():UIComponent
{
return _component;
}
public function set
component(component:UIComponent):void
{
_component = component;
}
}
}
A classe TextInput que implementa IComponent sempre recebe na variável
set component um componente do tipo TextInput, pois o Factory
ComponentFactory analisa o componente em questão com o método
getParentName().
Para adicionarmos um novo componente, temos que fazer o seguinte:
1) Criar uma classe que implementa IComponent
2) Definir o ParentName, que é o mesmo dos componentes do Flex
3) Adicionar a classe criada no array do método getComponents do
Factory.
Desta forma, podemos até “sofrer” um pouco para entendermos como tudo
funciona em conjunto, mas é garantido que após uma total compreensão de
tudo que está envolvido, podemos nos aproveitar dos padrões de projeto para
beneficiar nossos programas.
É válido lembrar também que esta obra aborda Action Script e não Padrões
de Projeto. Apenas apresentamos alguns (dos muitos existentes) e mostramos
suas soluções usando Action Script 3.0, provando assim que a linguagem é
bastante poderosa e que temos a ferramenta necessária para criarmos
aplicações com os recursos mais avançados sobre OO.
Para finalizar...
Para finalizar, eu deixo um link muito bom que comenta um pouco mais
sobre OO, e fala de princípios que todo programador deveria conhecer e
seguir. Acesse o link, e continuem seus estudos.
http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod