Programare orientată pe obiecte: Actionscript 3 - pachete, clase, metode și proprietăți

Pentru această lecție (e mai degrabă o lecție decât un tutorial) aveți nevoie de un IDE pentru Actionscript. Eu voi folosi FlashDevelopp dar dacă sunteți pe un Mac/Linux vă recomand varianta free de la FDT.
Bun, deschideți deci IDE-ul și creați un nou proiect AS3:



1. Pachete:
Dați-i un nume, gen "Tutorial". În structura pe directoare veți vedea un folder "src" unde există fișierul "Main.as". Ăsta e fișierul care se va compila. Dați click dreapta pe folderul "src" și creați un folder nou cu numele "lume". În folderul "lume" creați un nou folder cu numele "animale". Tocmai ați creat pachetul "lume.animale".


2. Clase
Dați click dreapta pe folderul "animale" și selectați "Add-NewClass".
O să vă apară fereastra asta:
Scrieți numele clasei: "Mamifer" și dați "ok". Tocmai ați creat clasa "Mamifer". Când veți deschide fișierul la dublu click, veți avea predefinit următorul format de clasă:

package lume.animale {
 public class Mamifer {
  public function Mamifer() {
  }
 }
}


După cum vedeți sus e numele pachetului: lume.animale, mai jos avem clasa publică mamifer, apoi constructorul public function Mamifer (){}.
Între acoladele de constructor scrieți
trace ("sunt un mamifer")
Metoda "trace" va afișa în output textul scris între paranteze.
Deschideți fișierul "Main.as", și scrieți sub acoloada de început de clasă o nouă instanță a clasei "Mamifer":
var mamifer:Mamifer;
care se traduce cu: "variabila mamifer de tipul Mamifer". La compilare (Project - TestProject) veți vedea că vă dă o eroare cu textul:
Error: Type was not found or was not a compile-time constant: Mamifer. var mamifer:Mamifer;
Asta pentru că nu ați importat pachetul cu clasa respectivă.
Orice clasă pe care vreți să o folosiți trebuie importată cu sintaxa:
import lume.animale.Mamifer;
ActionScript vă permite să importați mai multe clase din același pachet folosind un wildcard, adică:
import lume.animale.*;
Iar asta înseamnă că ați importat și puteți folosi toate clasele din pachetul "lume.animale". Nu toate limbajele permit această facilitate. De exemplu Haxe, care are o sintaxă asemănătoare cu ActionScript, necesită importarea per-clasă, ca în primul exemplu de mai sus.
După ce importați pachetul, compilați din nou, iar de data aceasta va merge, doar că veți avea un warning, care va spune:
Warning: var 'mamifer' will be scoped to the default namespace: Main: internal. It will not be visible outside of this package.
Asta pentru că proprietatea "mamifer" nu are nici un namespace (nici un tip). Compilatorul vă spune că automat va fi considerată publică. Pentru a suprima această eroare, puneți înainte de "var" cuvântul "private". Compilați din nou și de data aceasta nu veți mai avea erori. Dar nu veți avea nici un output. De ce? Pentru că deocamdată variabila scrisă nu are nici un constructor. Pentru a apela constructorul, variabila trebuie scrisă astfel:
private var mamifer:Mamifer=new Mamifer();
Acum avem un constructor al proprietății "mamifer" și la compilare o să vedem outputul pe care l-am scris în constructor.
Haide să populăm clasa "Mamifer" cu diferite proprietăți și metode pe care le voi explica în cele ce urmează:

package lume.animale {

public class Mamifer {

//constante statice:
static public const BIPED:String = "biped";
static public const PATRUPED:String = "patruped";

//private
private var nume:String = "nu am nume";
private var _sunet:String="nu am sunet"

//publica
public var numarDeOchi:int = 2;

//protejata
protected var _numarDePicioare:int = 4;

//constructor
public function Mamifer(param_nume:String="nu am nume") {
 nume = param_nume;
 var introducere:String="sunt un mamifer:"
 trace(introducere + nume);
}

//setare publica (tip java)
public function setSunet(param_sunet:String):void {
_sunet = param_sunet;
}

//getter public (tip java)
public function getSunet():String {
return _sunet
}

//getter public
public function getNume():String {
return nume;
}

//metoda protejata
protected function faZgomot():void {
trace (nume + " face " + getSunet());
}

//metoda privata:
private function pregatire():void {
trace (nume+" se apleaca, si...")
}

//metoda publica
public function faSunet():void {
pregatire();//private
faZgomot();//protejata
}

//getter tip as3
public function get numarDePicioare():int {
return _numarDePicioare;
}

//setter tip as3
public function set numarDePicioare(value:int):void {
_numarDePicioare = value;
}


//doar getter tip as3
public function get descriereDupaPicioare():String {
if (_numarDePicioare==4) {
return PATRUPED;
}
else if (_numarDePicioare == 2) {
return BIPED;
}
return "nu are un nume prestabilit"
}

//metoda statica
public static function definitie():void {
var definitie:String = "Clasă de vertebrate superioare care au corpul acoperit cu păr, nasc pui vii și îi hrănesc cu laptele lor";
trace(definitie);
trace("un mamifer poate fi " + BIPED + " sau " + PATRUPED);
}
}
}


3. Proprietăți și metode private; parametrii impliciți
După cum vedeți, am pus în constructorul clasei "Mamifer" un parametru implicit: variabila "nume" are valoarea "pisică". Parametrii impliciți sunt o facilitate a limbajului ActionScript. În alte limbaje, acest sistem trebui tratat altfel. Asta înseamnă că la instanțiere (adică atunci când creăm constructorul) nu e nevoie să punem neapărat un parametru.

Dacă în loc de
public function Mamifer(param_nume:String="nu am nume")
am fi avut
public function Mamifer(param_nume:String)
atunci codul
private var mamifer:Mamifer=new Mamifer();
ar fi aruncat o eroare de compilare, anunțând că aveți nevoie de cel puțin un parametru, și am fi fost obligați să scriem:
private var mamifer:Mamifer=new Mamifer("pisica");
Tot în constructor vedem că dăm o nouă valoare proprietății private "nume":
nume = param_nume;
.. și folosim o variabilă locală (disponibilă numai în interiorul acestei metode) pentru a face un trace.
O variabilă privată poate fi folosită oriunde în interiorul unei clase. Asta înseamnă că o putem folosi în interiorul oricărei metode private, publice sau protejate din această clasă.

4. Proprietățile și metodele publice pot fi apelate oriunde în interiorul clasei (ca și cele private) dar și din exteriorul clasei:
mamifer.faSunet()
va afisa:
"pisica se apleaca, si...
pisica face nu am sunet"
De asemenea, metodele publice pot fi suprascrise si citite oricand si oricum:
mamifer.numarDeOchi=2;
trace(mamifer.getNume() + " are" + mamifer.numarDeOchi +"ochi");
va afisa "pisica are2ochi"

Observați că tot publice sunt și metodele getSunet() respectiv setSunet(), astfel încât
mamifer.setSunet("miau");
mamifer.faSunet();
va afisa:
pisica se apleaca, si...
pisica face miau
pentru că faSunet() apeleaza metoda privata pregatire() și metoda protejata faZgomot(), care la rândul ei apeleza getSunet();


5. Getterii și setterii sunt metode publice sau private cu sintaxa de proprietăți.
De exemplu, în loc de:
public function getnumarDePicioare():int{
return _numarDePicioare;
}
public function setnumarDePicioare(value:int):void{
_numarDePicioare = value;
}
am scris:
public function get numarDePicioare():int{
return _numarDePicioare;
}
public function set numarDePicioare(value:int):void{
_numarDePicioare = value;
}

Diferența constă în apelare: în loc de
mamifer.getnumarDePicioare()
voi scrie
mamifer.numarDePicioare; //(fără acolade la sfârșit)
Și în loc de
mamifer.setnumarDePicioare(4);
voi scrie
mamifer.numarDePicioare=4;
În general, rolul unui getter sau al unui setter este de a proteja accesul la o variabila privata. Pe de-o parte, vrem sa avem acces la numarul de picioare al animalului, dar sa zicem ca nu vrem ca cineva sa seteze un numar impar de picioare. Astfel, în setter putem scrie
if (value%2!=0){
throw new Error("un mamifer nu poate avea un numar impar de picioare");
}
... adică dacă numărul dat e impar, aruncăm o eroare de cod. În Java de exemplu, nu există getteri și setteri propriu-ziși, cu sintaxă de proprietăți, așa cum e în C#, ci doar metode publice care pot seta sau returna o valoare. Desigur, același lucru l-am fi putut face și într-o functie de forma setnumarDePicioare(4), dar nu am mai fi avut un cod atât de "cool" când scriem mamifer.numarDePicioare=4; Da, singura diferență în cazul de față este doar de sintaxă.

6. Metode si proprietăți statice
În exemplul cu "Mamifer", proprietatea "BIPED" este o constată (are mereu aceeasi valoare) statică (nu poate fi modificată). Metoda "definitie()" este tot statică. Asta înseamnă că pentru ele nu avem nevoie de constructor. Putem apela direct:
Mamifer.definitie();
și va fi afișată definiția mamiferului sau
trace(Mamifer.BIPED);
pentru a afișa stringul "biped";
Atenție: într-o metodă statică nu putem folosi decât proprietăți statice, sau variabile locale

7. Proprietățile și metodele protejate sunt disponibile în interiorul clasei existente și în interiorul clasei extinse. Dar despre ele nu putem vorbi fără să extindem o clasă:
Dați click dreapta pe folderul "animale" și creați clasa "Pisică" astfel încât să arate cam așa:

package lume.animale{

public class Pisica extends Mamifer{

public function Pisica(){
super("pisica");
_numarDePicioare = 4;
setSunet("miau");
}

override protected function faZgomot():void{
trace (this.getSunet().toUpperCase());
}

override public function faSunet():void{
trace("ATENTIE! MIAUNA PISICA")
super.faSunet();
}

override public function set numarDePicioare(value:int):void{
if (value%2!=0){
throw new Error("un mamifer nu poate avea un numar impar de picioare");
}
super.numarDePicioare = value;
}
}
}

8. Polimorfism - ovveride și superiorizare
Vedeți în cod că aveți clasa "Pisică" ce extinde "Mamifer", iar în constructor am dat o valoare implicită parametrului "nume" folosind super("pisica"). Acum putem apela:
var pisica:Pisica = new Pisica();
fără nici un parametru;
Pentru că proprietatea _sunet din "Mamifer" era privată, tot în constructorul "Pisica" am dat o valoare implicită sunetului, cu
setSunet("miau");
Tot aici, am dat ovveride la metoda publica "faSunet()" adaugand un trace, și superiorizând-o. Asta înseamnă că după trace-ul meu, se va efectua restul functiei definite în "Mamifer"
La metoda faZgomot() însă, am dat ovveride fără a o superioriza, deci restul funcției din "Mamifer" nu se va mai executa. Astfel, codul
var pisica:Pisica = new Pisica();
pisica.faSunet();
va afisa:
sunt un mamifer:pisica
ATENTIE! MIAUNA PISICA
pisica se apleaca, si...
MIAU
Pentru a înțelege ce e polimorfismul, puteți șterge linia
var pisica:Pisica = new Pisica();
pisica.faSunet();
și o puteți înlocui cu
mamifer = new Pisica();
mamifer.faSunet();
Outputul va fi același. Pentru ca Pisica este o clasă ce extinde Mamifer, ea poate avea instanța inițială tot Mamifer

9. Să recapitulăm :

package{

import flash.display.Sprite;
import flash.events.Event;
import lume.animale.Mamifer;
import lume.animale.Pisica;

public class Main extends Sprite{

private var mamifer:Mamifer;

public function Main():void{

Mamifer.definitie();
/* Apeleaza o metoda statica si afiseaza:
Clasă de vertebrate superioare
care au corpul
acoperit cu păr,
nasc pui vii și îi hrănesc cu laptele lor
un mamifer poate fi biped sau patruped
*/
mamifer = new Mamifer("catel");
/*creaza o instanta a clasei mamifer,
afiseaza "sunt un mamifer:catel"*/
mamifer.setSunet("HAM");
/*seteaza un sunet pentru catel*/
mamifer.faSunet();
/*apleaza pregatire() si faZgomot(),
* afiseaza sunt un mamifer:catel
* catel se apleaca, si...
* catel face HAM
* */
mamifer = new Pisica();
/* creaza o instanta a clasei Pisica()
cu un nume implicit;*/
mamifer.faSunet();
/*apeleaza o metoda publica marcata
cu ovveride.*/
trace(mamifer.descriereDupaPicioare);
/*afiseaza un string stabilit
* in functie de numarul de picioare*/
}
}
}

Outputul va fi:
Clasă de vertebrate superioare care au corpul acoperit cu păr, nasc pui vii și îi hrănesc cu laptele lor
un mamifer poate fi biped sau patruped
sunt un mamifer:catel
catel se apleaca, si...
catel face HAM
sunt un mamifer:pisica
ATENTIE! MIAUNA PISICA
pisica se apleaca, si...
MIAU
patruped

Niciun comentariu:

Trimiteți un comentariu