La programmazione ad oggetti on PHP
Programmazione ad oggetti o programmazione strutturale?
La risposta è semplice: programmazione ad oggetti...il problema è che la programmazione ad oggetti (OOP d'ora in avanti) non è supportata in modo molto "convincente" dalla versione attuale di PHP, e soprattutto sembra essere una rgomento ostico per molti. La pillola che mi stò accingendo a scrivere, non pretende di essere la guida più esausiva del mondo sull'argomento dell'OOP, ma comprenderà una spiegazione teorica (nulla di difficile), ed un piccolo esempio pratico per capire la sintassi da utilizzare in PHP nella creazione di una classe. Premetto che quanto scritto servirà solo fino alla release ufficiale di PHP5, che comprenderà un supporto molto più completo all'OOP e delle piccole variazioni nella sintassi.
Cominciamo con la teoria:
Per capire cosa sia la OOP e come utilizzarla nei nosti script (che possono anche essere definiti programmi, in caso qualcuno decida di sviluppare in PHP-GTK), è fondamentale comprendere i seguenti concetti:
- CLASSE: cominciamo con il concetto fondamentale: cos'è una classe?? Una classe altro non è che un modello astratto per diversi oggetti che hanno caratteristiche simili, ed incorpora le caratteristiche di una famiglia particolare di oggetti. Il tutto è molto stringato (di fatti si tratta solamente di una pillola), ma capirete molto meglio seguendo l'esempio.
- ISTANZA/OGGETTO: istanza ed oggetto sono sinonomi: mentre una classe è la rappresentazione generale dell'oggetto, un'istanza ne è la rappresentazione effettiva, concreta.
- EREDITARIETA': l'ereditarietà è uno dei concetti più importanti legati alla programmazione ad oggetti, ed ha un effetto immediato sulla struttura e sulla stesura delle applicazioni. è un meccanismo molto potente, che permettedi specificare, nella definizione di una classe, solo le differenze rispetto ad un classe già esistente: l'ereditarietà da accesso alle informazioni relative a quest'ultima. L'ereditarietà, solitamente, ordina in una rigita ma efficente gerarchia le classi. Chiameremo superclasse la classe padre dalla quale una sottoclasse erediterà metodi e proprietà.
- VARIABILE DI ISTANZA e VARIABILI DI CLASSE: la differenza è minima ma importante: le variabili di classe definiscono le proprietà statiche ed inalterabili (questo non è vero per PHP, purtroppo) di un classe, mentre le variabili di istanza servono a definire le proprietà particolari di ogni oggetto (solitamente è compito del costruttore la loro inizializzazione).
- METODO: un metodo è una funzioe che rappresenta una "capacità" di una classe e quindi delle sue istanze e delle istanze delle sue eventuali sottoclassi. Nulla di più. Esitono metodi particolari che descriveremo in seguito.
- COSTRUTTORE/DISTRUTTORE: un costruttore è una funzione che viene chiamata durante la creazione di un'istanza da un classe, e, normalmente, serve a definirne lo stato. Un costruttore non è obbligatorio. PHP non ha, invece, distruttori, funzioni chiamate quando un oggetto viene distrutto, quando cioè viene effettivamente eliminato dalla memoria, solitamente quando non vi sono più riferimenti.
Appresi questi concetti (o cestinati, fa lo stesso, ma non sarete mai programmatori seri senza sapere un bel po di teoria), possiamo passare all'analisi di una classe (inutile). Scrivo di seguito il listato completo, passerò successivamente a spiegarvi il concetto che sta alla base di questa classe ed il modo in cui utilizzarla:
Codice PHP:
class essereVivente{
var $specie;
var $ambiente;
//...ne avrei potute aggiungere a migliaia...
function essereVivente($specie,$ambiente){
$this->specie = $specie;
$this->ambiente = $ambiente;
}
function muori(){
echo "se continueranno a morire esseri della mia specie (".$this->specie.") ci estingueremo\n";
}
function doveVivo(){
echo "vivo in".$this->ambiente;
}
//anche di metodi ne potrei aggiungere a migliaia...
}
class animale extends essereVivente{
var $peso;
var $altezza;
var $gasRespirato = "ossigeno";
var $arti;
function animale($specie,$ambiente,$peso,$altezza,$arti){
$this->essereVivente($specie,$ambiente);
$this->specie = $specie;
$this->peso = $peso;
$this->altezza = $altezza;
$this->arti = $arti;
echo "Sono nato!!";
}
function respira(){
echo "Sono un essere vivente, e stò respirando ".$this->gasRespirato;
}
function salta($spazio){
if($this->altezza * 5 > $spazio){
echo "Ho saltato ".$spazio." metri";
}else{
echo "Al massimo posso saltare ".($this->altezza*5)." metri!";
}
}
}
class essereUmano extends animale{
var $lingua;
var $sport;
var $nome;
var $eta;
var $sesso;
function essereUmano($nome,$sesso,$ambiente,$peso,$altezza,$eta,$lingua,$sport){
$this->animale("Essere umano",$ambiente,$peso,$altezza,4);
$this->lingua = $lingua;
$this->eta = $eta;
$this->sport = $sport;
$this->nome = $nome;
$this->sesso = $sesso;
}
function guida(){
if($this->eta < 18){
echo "Non sono neanche maggiorenne, come posso guidare??";
}else{
echo "Segno della croce e via!!Anche se non ho ancora preso la patente!";
}
}
function parla($frase){
echo $frase."\nSe vuoi te la dirò anche in ".$this->lingua;
}
}
class donna extends essereUmano{
function donna($nome,$ambiente,$altezza,$lingua){
$this->essereUmano($nome,"femmina",$ambiente,"fruscello",$altezza,"una giovincella",$lingua,"nessuno");
}
function guida(){
echo "3 morti e 15 feriti...";
}
function pensa(){
echo "Scusa?Che hai detto??";
}
function maQuantiAnniHai(){
echo $this->eta;
}
}
class uomo extends essereUmano{
function uomo($nome,$ambiente,$peso,$altezza,$eta,$lingua,$sport){
$this->essereUmano($nome,"maschio",$ambiente,$peso,$altezza,$eta,$lingua,$sport);
}
function pensa(){
echo "Sono troppo un genio, che penso a fare??";
}
function gioca(){
echo "Stò già giocando a ".$this->sport." !";
}
}
bene...copiate queste classi completamente inutili su un documento PHP e visualizzatelo: la pagina dovrebbe essere completamente vuota. Questo perchè avete creato la CLASSE, nessuna ISTANZA, quindi tutto ciò che è presente sul documento è solamente il modello astratto (ricordate??), nulla di "reale" ed attivo. Le classi che ho creato sopra, rappresentano molto astrattamente quello che potrebbe essere un albero delle specie viventi. Abbiamo la classe essereVivente che definisce alcuni tratti comuni tipici delle varie specie, siano queste animali, vegetali o cos'altro. Una seconda classe animale(che eredita dalla prima) che definisce proprietà tipiche di un solo ramo degli esseri viventi: quello appunto degli animali. La classe essereUmano eredita da animale, ed anchessa definisce delle caratteristiche generali per un essere umano, tipiche di entrambi i sessi. le ultime due, donna e uomo, definiscono in modo astratto l'essere umano uomo e l'essere umano donna, e per questo motivo ereditano dalla classe essereUmano. Il tutto può essere riassunto nello schema seguente (che vi consiglio di preparare ogni volta che decidete di sviluppare un complesso sistema di classi):
codice:
essereVivente
|
|____ animale
|
|____ essereUmano
|
|____ donna
|
|____ uomo
lo schema precedente definisce la struttura del nostro pseudoprogramma. Guardando velocemente questo grafico, possiamo capire che, in caso volessimo utilizzare la classe donna nelle nostre applicazioni, questa avrà TUTTI i metodi e le proprietà delle classi essereVivente,animale e essereUmano (le sue superclassi,ricordate??), ma nulla in comune con la classe uomo. Passiamo ora all'analisi line-by-line del codice
Riga 1:
Codice PHP:
class essereVivente{
questa è la sintassi minima per la creazione di una classe. Di seguito trovate un piccolo schemino che rappresenta la sintassi corretta (tra parentesi quadre la parte opzionale):
codice:
class nome_classe [extends nome_superclasse]{
}
Come potete notare, non è obbligatorio che una classe abbia una superclasse, ma in caso succeda, a superclasse può essere una sola. Infatti PHP non supporta l'ereditarietà multipla (punto di forza di C++ e Python), che permette di ereditare metodi e proprietà da più genitori, come il più blasonato Java. Notiamo che nome_classe segue le stesse regole per la definizione di un nome di una funzione o di una variabile (qundi niente numerini all'inizio ecc...), e non può assumere il nome di una funzione già definita.
Righe 2-3:
Codice PHP:
var $specie;
var $ambiente;
Qui, prima di definire il costruttore o ogni altra funzione, definiamo le variabili di classe o di istanza. Notate che, differentemente da altri linguaggi di programmazione, PHP permrette di creare solo variabili pubbliche, a cui vi si può accedere in lettuara ed in scrittura d qualsiasi parte dello script. La sintassi corretta per la definizione di una variabile è la seguente:
codice:
var nome_variabile [= valore];
Come potete vedere, è obbligatorio porre la "parolina" var PRIMA di scrivere il nome della vostra variabile, pena un errore di debug. Il valore non è obbligatorio, questo perchè, in questo modo, è possibile differenziare (virtualmente,non praticamente per ora) le variabili di istanza (quelle senza valore, che saranno inizializzate in seguito) e le variabili di classe (quelle con un valore iniziale che, in teoria, non dovrebbe essere variato). Notiamo che le variabili possono assumere (in caso siano di classe, quindi inizializzate direttamente nella definizione) solo i seguenti tipi di valore:
numerico (1, 100, 10.001, ecc...)
stringa ("1", "ciao", "casc casmcsa clacsòlmcòasc", ecc...)
array (array(), array("chiave" => valore,...), array(valore,valore,...), ecc...)
costanti (PHP_OS, ecc...)
quindi non possiamo fare riferimento a variabili globali, non possiamo scrivere espressioni del tipo,
Codice PHP:
//ERRATO!
var $figa_o_no = (($GLOBALS["misura_seno"]$_POST["misura_culo"]-$_SERVER["misura_fianchi"])/2 == $_SERVER["misura_fianchi"]);
//ERRATO!
non possiamo neppure utilizzare funzioni (ne user defined, ne globali) e, cosa da tenere molto inc onsiderazione, non possiamo istanziare nuove classi (per chi si sta chiedendo: "come si istanzia una classe??" veda più avanti).
Riga 6:
Codice PHP:
function essereVivente($specie,$ambiente){
Come potete notare, questa non è altro che la definizione di una normalissima funzione. Infatti, se ci troviamo all'interno della definizione di una classe, tutte le funzioni definite (che seguono le normali regole di definizione di una funzione in PHP) diventano automaticamente metodi della classe. E' inutile che vi scriva la sintassi corretta di una funzione, ma in questa riga c'è da tenere conto ancora di una cosa: come potete vedere, il nome della funzione è lo stesso del nome della classe che lo contiene. Non è un errore,ma bensi è il modo in cui di definisce un costruttore in PHP. Ricordo, per i neofiti provenienti dall'oscuro mondo Java, che in PHP NON è attualemente possibile eseguire l'overloading dei metodi. Ricordate quindi: l'unica cosa che differenzia un metodo da un costruttore, è che quest'ultimo DEVE avere il nome della classe nel quale è definito. Come potete notare, il costruttore non restituisce nulla, e può essere anche ommesso.