DevSecOps

22 jan, 2008

Programação orientada a objeto em ActionScript 3.0

Publicidade

Este artigo é focado ao início de desenvolvimento de aplicações Flash orientado a objetos.

Ironicamente, usuários do Flash que são novos para programação orientada a objeto (OOP) são geralmente familiarizados com muitos conceitos de orientação a objeto, mas sem saber seus nomes formais.

Aqui desvendamos algumas das terminologias e trazemos mais programadores para acelerar conceitos da chave OOP. Ainda serve como uma visão geral de alto nível de OOP em Flash para programadores experientes que estão fazendo sua primeira incursão dentro do desenvolvimento Flash.

O que é um objeto?

Um objeto é a representação de uma entidade em um sistema. Resumindo, tudo são objetos. No Flash existe uma série de objetos prontos que facilitam o desenvolvimento e tem aspectos condizentes ao objeto.

Com certeza, não encontraremos nenhum campo de texto executando um play, ou um MovieClip exibindo um texto. Isso porque objetos contem características relativas ao próprio objeto.

Se pensarmos em pessoas, elas podem falar, andar… voar não seria uma característica do objeto pessoa.

Um objeto então é resumido em suas características e no que ele pode executar.

Por que objetos?

A idéia de OOP (Oriented Object Programming) é modelar um sistema baseado em sistemas, o que representa maior adaptação.

Cada objeto deve ser auto-explicativo e fácil de entender. Mas qual o conceito de modelagem? Isso simplesmente se refere em como o sistema é desenhado, um sistema desenhado em OOP se refere a uma aplicação onde cada unidade possui características e comportamentos relacionados ao objeto.

Imagine que você pode mudar características de cada ser vivo na Terra. Porém, cada ser vivo tem suas particularidades e coisas em comum. Por exemplo, todos os seres vivos respiram, logo poderíamos ter um objeto de nome SerVivo, pessoas são seres vivos? Sim! Então dizemos que o objeto pessoa estende o objeto ser vivo. Essa definição poderia ser ainda mais bem dividida se colocássemos um objeto mamífero entre pessoa e ser vivo. Pensando assim teríamos um sistema bem dividido o que apresentaria menos redundância de código e menos manutenção, caso precisássemos trocar apenas um dos objetos.

OOP x Estruturada

OOP foi resumido nas linhas acima e estudaremos um pouco mais neste artigo, bem como nos posteriores a este. Linguagem estruturada não é um termo conhecido por todos, mas aplicado por aqueles que já desenvolviam no Flash sem a utilização da OOP. Resumindo, linguagem estruturada (procedural), é uma linguagem baseada em procedimentos, funções.

Para esclarecer bem a diferença entre ambos, imagine uma galeria de fotos. Teríamos minimamente os seguintes itens:

  • Foto Pequena
  • Foto Grande
  • Botão de Navegação
  • Barra de Progresso para os devidos Pré-carregamentos
  • Galeria com o devido Efeito de Transições

Em OOP dividiríamos cada um dos objetos em suas devidas classes, o que indica que, caso quiséssemos reutilizar a galeria e trocar (ou adaptar) qualquer um dos elementos, poderíamos simplesmente editar a classe em questão ou estender suas capacidades (herança, discutiremos mais tarde).

Em linguagem estruturada não, teríamos que copiar o arquivo fonte (.fla) e adaptar cada uma das funções ao nosso uso atual, o que normalmente acaba em grande e tedioso re-trabalho, sem contar que qualquer alteração errada pode afetar todo o resto da aplicação, no caso, essa simples galeria.

Entendendo uma Classe

Uma aplicação OOP deve ser tão dividida quanto possível (encapsulamento), para que cada objeto haja independente dos outros.

Imaginando uma casa, teríamos diversos componentes, como, número de salas, sistema de ar condicionado, sistema de aquecimento, sistema elétrico, etc.

Todos esses elementos em conjunto devem formar uma casa, e o arquiteto (e os demais experts em cada assunto) é responsável pelo desenho de cada um dos sistemas.

Se o arquiteto quer fazer uma nova casa, não é necessário recomeçar tudo novamente. A maioria dos componentes da casa já estão pré-construídos, usando objetos pré-construídos a complexidade de construção de uma casa é altamente reduzido. Objetos individuais podem ser substituídos, sem que haja necessidade de reconstruir todo o sistema.

Observem como criar uma simples classe no exemplo abaixo:

package com.leandroamano.helloWorld {
	public class PrimeiroExemplo {
		public function PrimeiroExemplo(frase:String = "Teste!") {
			trace(frase);
		}
	}
}

O arquivo acima, PrimeiroExemplo.as demonstra uma simples classe em AS3, com um único método (construtor) e nenhuma propriedade, utilizei o menos código possível para construção de uma classe:

Primeiro definimos o pacote em que o arquivo .as se encontrará para que o .fla o encontre, neste caso possuímos uma pasta de nome com, dentro desta leandroamano e por último helloWorld. Repare que a seqüência de pastas é feita através da sintaxe de pontos e vem após a palavra chava package seguida de um espaço, este é o bloco principal da classe.

Dentro do bloco package definimos o nome da classe (PrimeiroExemplo), com acesso público (temos outros acessos modificadores, veremos mais tarde).

Dentro do bloco da classe o método construtor, método que possui o mesmo nome da classe, inclusive mantém a mesma capitalização. Este método é executado logo que algum objeto o inicia com o operador new.

Repare que dentro do método construtor temos um trace que executará a string passada no construtor, caso não passemos nada, executará um output com a mensagem “teste”.

Para testar esse código rapidamente, abra um arquivo fla e passe esse código a ele:

import com.leandroamano.helloWorld.PrimeiroExemplo;
var teste:PrimeiroExemplo = new PrimeiroExemplo();
var teste2:PrimeiroExemplo = new PrimeiroExemplo("Webdesigner");

Herança

Talvez o principal e mais importante conceito da OOP, herança define a habilidade de herdar propriedades e métodos de um objeto, estendendo a funcionalidade de um objeto, transformando-o em outro (relação super-classe/sub-classe).

A regra mais simples para se pensar em relações entre classes para aplicar herança é pensando assim: se você puder falar, minha classe A é uma classe B, você provavelmente poderá aplicar herança, observe:

O homem é um ser vivo, o futebol é um esporte, etc.

Um exemplo simples:

//classe Mamiferos

package {
	public class Mamiferos {
		
		public function Mamiferos() {
			
		}
		public function andar(){
			trace("Mamifero andando");
		}
	}
}

//classe Humano

package {
	public class Humano extends Mamiferos {
		public function Humano() {
			trace("Também sou um mamífero!");
		}
	}

}

Observe que nossa classe Mamiferos que neste caso é o topo da hierarquia de classes que derivam este tipo possui apenas o método construtor e o método andar, automaticamente nosso objeto Humano possui também o método andar. Em ActionScript 3.0 é possível explicitamente sobrescrever este método adicionando outra funcionalidade, já que cada mamífero pode locomover-se de uma maneira diferente (ou não), para isso utilize a palavra chave override:

package {
	public class Humano extends Mamiferos {
		public function Humano() {
			trace("Também sou um mamífero!");
		}
		public override function andar() {
			trace("Homem andando");
		}
	}

}

Encapsulamento

Outro importante conceito da OPP é chamado de encapsulamento. Isso basicamente define quais métodos e propriedades podem ser acessadas de fora da própria classe e como podem ser acessados. Alguns de vocês devem estar se perguntando, mas pra que isso? Resposta simples. Pense numa televisão. Agora pense o que você necessita saber para fazer ela funcionar… Você, usuário da TV, necessita conhecer algumas das funcionalidades que a TV lhe proporciona para utiliza-la, o usuário não precisa saber para que servem os chips, nem tão pouco seus fios internos. O usuário de sua classe também, em alguns casos, permitir acesso total a sua classe pode acabar com seu bom funcionamento.

Os acessos modificadores em ActionScript3 são:

public: podem acessadas de qualquer lugar.

private: podem ser acessadas somente dentro da própria classe.

internal: podem ser acessadas somente em classes do mesmo pacote.

protected: podem ser acessadas somente dentro da própria classe e subclasses da mesma.

Métodos getter/setter são recomendáveis para o uso de encapsulamento de propriedades, não veremos isso neste artigo, porém, recomendo o estudo sobre o tema.

Polimorfismo

O último dos conceitos deste artigo é o polimorfismo, este conceito é baseado na idéia de que diferentes classes podem implementar o mesmo nome nos métodos, porém, com funcionalidades diferentes.

O mais correto aqui seria utilizar interfaces e suas implementações utilizando também recursos de casting, veremos isso num futuro artigo. Para aproveitamento dos assuntos aprendidos hoje utilizaremos a herança para isso.

Vamos criar duas classes (Gato e Cachorro) e aproveitaremos a classe Humano e a classe Mamíferos:

package {
	public class Gato extends Mamiferos {
		public override function andar() {
			trace("gato andando");
		}
	}

}

package {
	public class Cachorro extends Mamiferos {
		public override function andar() {
			trace("cachorro caminhando");
		}
	}

}

package {
	public class Humano extends Mamiferos {
		public override function andar() {
			trace("Homem andando");
		}
	}

}

package {
	public class Mamiferos {
		
		public function Mamiferos() {
			
		}
		public function andar(){
			trace("Mamifero andando");
		}
	}
}

Até aqui possuímos quatro classes, a classe generalizada Mamíferos e três classes especializadas a partir de Mamíferos, Humano, Gato e Cachorro, criaremos agora a classe de controle que chamará os métodos andar de cada subclasse de Mamíferos:

package {
	public class ControlaMamiferos {
		public function ControlaMamiferos() {
			var mamiferos:Array = new Array(new Humano(), new Gato(), new Cachorro());
			for each (var i:* in mamiferos){
				executa(i);
			}
		}
		public function executa(mamifero:Mamiferos):void {
			mamifero.andar();
		}
	}
}

No frame de um arquivo .fla:

var control:ControlaMamiferos = new ControlaMamiferos();

O resultado no output é o chamado dos três diferentes métodos andar() de cada objeto.

Não esqueça que, para este uso, necessitamos sobrescrever cada um dos métodos da classe super Mamíferos. Ficou fácil implementar o polimorfismo em ActionScript 3.0.