Scrivere buon codice OO in Adobe Flash
Venerdì 19 Ottobre, 2007Ecco alcuni consigli su come scrivere un buon codice Object Oriented (OO) in Adobe Flash, soprattutto per chi ancora usa la versione MX in attesa di passare alla CS3.
Organizzare le cartelle delle classi
Prima di tutto l'organizzazione delle classi rende il lavoro di manutenzione del codice estremamente più semplice. Inoltre è possibile creare una vera e propria libreria personale da poter riutilizzare in altri progetti. Flash usa una nomenclatura legata al filesystem, quindi l'organizzazione in cartelle si rifletterà anche sull'importazione delle classi. Se ad esempio creiamo la sequenza di cartelle "mylibrary/grafica/plot" e inseriamo una nostra classe ActionScript "PlotClass.as", quando andremo ad importare la classe dovremmo usare:
-
import mylibrary.grafica.plot.PlotClass;
Se la libreria (cartella) "mylibrary" non si trova nella cartella del nostro filmato o progetto, usare l'impostazioni di pubblicazione di Flash per selezionare il percorso:
Incapsulare il codice: usare le proprietÃ
La fretta è sempre "cattiva consigliera", quindi è bene spendere del tempo in più per poter raccogliere i frutti del nostro impegno più avanti. Quando si sviluppa una classe in ActionScript si tende a "mostrare" a tutti le nostre propreità , anche quelle che vengono di fatto utilizzate solo internamente alla classe.
-
class MiaClasse {
-
var mio_valore:Number = 10;
-
function MiaClasse() {}
-
}
Tutto ciò è un male in quanto rende il nostro codice estremante instabile e soggetto ad errori, anche se siamo noi ad usare gli oggetti in questione. La regola dell'incapsulmento permette invece di controllare l'impostazione delle proprietà di una classe rendondola estremamente più stabile.
-
class MiaClasse {
-
private var __mio_valore:Number = 10;
-
function MiaClasse() {}
-
public function get MioValore():Number {
-
return(__mio_valore);
-
}
-
public function set MioValore(v:Number) {
-
// pssst: usando l'operatore ternario...
-
// __mio_valore = (v> 5 && v <20)?v:__mio_valore;
-
if( v> 5 && v <20) {
-
__mio_valore = v;
-
}
-
}
-
}
In questo esempio la proprietà MioValore, come viene vista dall'esterno, viene controllata dalla function set in modo tale che possa assumere solo i valori compresi tra 5 e 20. Questo significa che all'interno della nostra classe noi faremo riferimento esclusivamente a __mio_valore o this.__mio_valore se preferite. Tuttavia saremo sempre sicuri che la "variabile" __mio_valore sarà compresa tra 5 e 20, a meno che non siamo noi stessi, all'interno della classe, a modificarne erroneamente il valore.
Inoltre l'uso delle proprietà , ovvero delle function get e set, permette di creare proprietà in sola lettura, in sola scrittura o entrambe, il chè non è da poco.
Usare Private e Public quando serve
I metodi usati solo internamente alla classe non devono essere esposti anche all'esterno. Prima di tutto perchè non serve a nulla! Secondo, perchè di fatto non fanno parte dell'interfaccia dell'oggetto. Quando si rilascia una versione di una Classe implicitamente si certifica che i metodi e le proprietà che la classe espone (l'interfaccia) saranno mantenute nelle future release dell'oggetto. Ovviamente quello che può cambiare è la loro implementazione, ovvvero il codice sottostante, ma non il nome o i parametri dell nostro metodo o proprietà . Usando i metodi private, invece, questi saranno visibili solo dall'interno del nostro oggetto. Noi li potremmo usare ed, eventualmente, modificare completamente, senza contravvenire alla regola fondamentale della programmazione ad oggetti.
Ereditare
L'ereditarietà (inheritance), ovvero la caratteristica di un oggetto di discendere da un altro e quindi "ereditare" tutte le caratteristiche dell'oggetto "madre", è un eccezionale strumento della programmazione ad oggetti. La costruzione di gerarchie, tuttavia, richiede molta esperienza, onde evitare di create inutili sottoinsiemi che rendono il codice un enorme pachiderma presto ingestibile. Spesso si creano le classi madre (le IUnknown) dopo una lunga analisi o dopo essersi accorti di qualche "ridondanza" durante la creazione di classi.
Rimane comunque il fatto che usarla, con cautela, permette di estendere in pochi secondi tutti gli oggetti filgi di una classe. ActionScript utilizza la keyword extends per creare ereditarietà tra una classe ed un'altra.
-
// File ClasseMadre.as
-
class ClasseMadre {
-
function ClasseMadre() {}
-
public function Addiziona(a:Number, b:Numer):Number {
-
return(a+b);
-
}
-
}
-
// File Figlio.as
-
import ClasseMadre;
-
class Figlio extends ClasseMadre {
-
function Figlio() {}
-
}
In questo esempo la classe Figlio dispone automaticamente del metodo Addiziona(), ereditato appunto dalla classe madre ClasseMadre. Particolarmente utile è la possibilità di far sempre riferimento alla classe madre tramite la keyword super, così da permettere l'overwrite dei metodi e proprietà ereditati.
-
// File ClasseMadre.as
-
class ClasseMadre {
-
function ClasseMadre() {}
-
public function Addiziona(a:Number, b:Numer):Number {
-
return(a+b);
-
}
-
}
-
// File Figlio.as
-
import ClasseMadre;
-
class Figlio extends ClasseMadre {
-
function Figlio() {}
-
public function Addiziona(a:Number, b:Numer, c:Number):Number {
-
return( super.Addiziona(a,b)+c );
-
}
-
}
Questa, a mio avviso, è una delle tante bellezze dello sviluppo ad oggetti!
Soluzioni
Se la classe che stiamo scrivendo è collegata ad un MovieClip, diventando di fatto una sua estensione, ActionScript impone che qualsiasi altro oggetto (MovieClip o TextField) contenuto nel MovieClip padre, sia referenziato all'inizio della classe. Se ad esempio abbiamo esteso un MovieClip che contiente un secondo MovieClip identificato dal nome secondo_mc, quando dichiariamo la classe dobbiamo inserire un riferimento a questo MovieClip in questo semplice modo:
-
class MiaClasse extends MovieClip {
-
private var secondo_mc:MovieClip;
-
function MiaClasse() {}
-
//
-
public function Refresh() {
-
// ActionScript darebbe errore se non avessimo dichiarato questa variabile/puntatore all'inizio
-
this.secondo_mc._x = 10;
-
}
-
}
Quando gli oggetti da manipolare sono noti non esiste nessun problema. Tuttavia può capitare di dover create runtime un MovieClip e di conseguenza non aver previsto nessuna riga di dichiarazione iniziale. Ad esempio questo codice non funzionerebbe:
-
class MiaClasse extends MovieClip {
-
function MiaClasse() {}
-
//
-
public function CreaMovie() {
-
createEmptyMovieClip("secondo_mc", 100);
-
// ActionScript non conosce questo oggetto
-
this.secondo_mc._x = 10;
-
}
-
}
Questo è un caso molto frequente, immaginate il seguente codice:
-
class MiaClasse extends MovieClip {
-
function MiaClasse() {}
-
//
-
public function CreaMovie() {
-
for(var i = 0; i <50; i++) {
-
createEmptyMovieClip("item"+i, i);
-
}
-
}
-
}
In questo caso, come in altri, è possibile accedere ad un oggetto come se fosse un indice di un array, ovvero:
-
class MiaClasse extends MovieClip {
-
function MiaClasse() {}
-
//
-
public function CreaMovie() {
-
for(var i = 0; i <50; i++) {
-
createEmptyMovieClip("item"+i, i);
-
this["item"+i]._x = 10;
-
}
-
}
-
}
Questa è una tecnica assai utile in numerose circostanze.
Versioning
Un'ultima cosa utile che mi permetto di suggerire è il versioning, cioè l'attenzione a mantenere uno storico delle modifiche apportate. Quando si ha a che fare con una serie di classi queste tenderanno ad evolvere nel tempo. Buona regola è quindi di dotarsi di un programma (SCCS [Source Code Control System], CVS, Subversion per MacOS/Linux/Unix o FileHamster sotto Windows) in grado di mantenere il versioning dei file delle varie release che rilasciamo.
Oltre che commentare il codice e le versioni/revisioni consiglio di impostare una variabile all'interno delle classi (sullo stile Javascript) in modo da rendere "parlante" il codice stesso dell'oggetto:
-
class MiaClasse {
-
private var __release:String = "1.0";
-
function MiaClasse() {}
-
}
Inclusioni e costanti
È possibile includere codice direttamente nelle classi. Utile a livello di organizzazione, sul tipo:
-
class MiaClasse extends MovieClip {
-
#include "ClassVersion.as"
-
}
Se poi vi mancano le #define (davvero un peccato che non siano supportate!) del C, ecco una soluzione abbastanza decororsa:
-
public static var ERROR_1:String = "Errore tipo 1";
-
// Oppure - da codice Adobe
-
// values for command types for _cmdQueue
-
static var PLAY:Number = 0;
-
static var LOAD:Number = 1;
-
static var PAUSE:Number = 2;
-
static var STOP:Number = 3;
-
static var SEEK:Number = 4;





















Actionscript 3.0 for beginners: lesson #2 | Undolog.com ha detto:
[...] proprietà pubbliche, sfruttando l’incapsulamento proprio della programmazione OO (vedi Scrivere buon codice OO in Adobe Flash). __player e __stop indicano rispettivamente il giocatore attivo e lo stato del [...]