NOME |
perlboot - Introduzione alla tecnologia Orientata agli Oggetti (titolo originale: Beginner's Object-Oriented Tutorial)
Se non avete già una certa familiarità con la tecnologia ad oggetti degli altri linguaggi di programmazione, parte della documentazione sulla OOP in Perl potrebbe essere un po' intimidatoria: the perlobj manpage, una guida di riferimento sull'utilizzo degli oggetti e the perltoot manpage che introduce il lettore alle particolarità della tecnologia ad oggeti del Perl con un taglio introduttivo.
Partiamo dunque con un approccio differente, non assumendo a priori alcuna esperienza con gli oggetti. Può aiutare la conoscenza delle subroutine (the perlsub manpage), dei riferimenti (the perlref manpage e seguenti), e dei package (the perlmod manpage), dunque se non la possedete già, conviene che vi familiarizzate con questi argomenti.
Per un attimo, lasciamo parlare gli animali:
sub Mucca::parla { print "la Mucca fa muuuu!\n"; } sub Cavallo::parla { print "il Cavallo fa hiiii!\n"; } sub Pecora::parla { print "la Pecora fa beee!\n" }
Mucca::parla; Cavallo::parla; Pecora::parla;
Questo produce:
la Mucca fa muuuu! il Cavallo fa hiiii! la Pecora fa beee!
Fino a qui niente di spettacolare. Semplici subroutine, anche se da package separati e chiamate usando il nome completo del package. Creiamo dunque un intero pascolo:
# Mucca::parla, Cavallo::parla, Pecora::parla come prima @pascolo = qw(Mucca Mucca Cavallo Pecora Pecora); foreach $animale (@pascolo) { &{$animale."::parla"}; }
Questo produce:
la Mucca fa muuuu! la Mucca fa muuuu! il Cavallo fa hiiii! la Pecora fa beee! la Pecora fa beee!
Urca. Questa dereferenziazione di un riferimento simbolico a codice è
piuttosto brutta. Ci stiamo affidando al comportamento dato da no strict subs
,
che non è certamente raccomandato per programmi di una certa dimensione.
E perché era necessario? Perché il nome del package sembra essere
inseparabile dal nome della subroutine che vogliamo invocare all'interno
di quel package.
O no?
Per ora, diciamo che Classe->metodo
invoca la subroutine
metodo
nel package Classe
. (In questo contesto, ``Classe'' è
usata nel suo significato ``categorico'', non nel suo significato ``scolastico'').
Questo non è del tutto preciso, ma lo rivedremo a tempo debito.
Per ora, utilizziamolo in questa maniera:
# Mucca::parla, Cavallo::parla, Pecora::parla come prima Mucca->parla; Cavallo->parla; Pecora->parla;
Ancora una volta, questo produce:
la Mucca fa muuuu! il Cavallo fa hiiii! la Pecora fa beee!
Questo non è ancora divertente. Stesso numero di caratteri, solo costanti, nessuna variabile. Malgrado tutto, le parti ora sono separabili. Osservate:
$a = "Mucca"; $a->parla; # invoca Mucca->parla
Ahh! Ora che il nome del package è stato separato dal nome della
subroutine, possiamo usare un nome di package variabile.
E questa volta abbiamo qualcosa che funziona anche quando
use strict refs
è in funzione.
Prendiamo questa nuova invocazione a freccia e utilizziamola nell'esempio del cortile:
sub Mucca::parla { print "la Mucca fa muuuu!\n"; } sub Cavallo::parla { print "il Cavallo fa hiiii!\n"; } sub Pecora::parla { print "la Pecora fa beee!\n" }
@pascolo = qw(Mucca Mucca Cavallo Pecora Pecora); froeach $animale (@pascolo) { $animale->parla; }
Ecco! Ora abbiamo tutti gli animali che parlano, e in modo sicuro, senza l'uso di riferimenti simbolici a codice.
Ma guardate tutto quel codice in comune.
Ognuna delle routine parla
ha infatti una struttura similare:
un operatore print
ed una stringa che contiene testo identico
tranne che per due parole. Sarebbe carino poter raccogliere a
fattor comune le similitudini, nel caso si decida in seguito di mettere
``dice'' al posto di ``fa''.
E in effetti abbiamo un sistema per farlo senza troppo sforzo, anche se dobbiamo imparare qualcosa in più su cosa faccia davvero la freccia per invocare i metodi.
L'invocazione di:
Classe->metodo(@argomenti)
tenta di invocare la subroutine Classe::metodo
come se avessimo scritto:
Classe::metodo("Classe", @argomenti);
(Se la subroutine non può essere trovata, l'``ereditarietà'' entra in
funzione, ma ci arriveremo più avanti). Questo significa che riceviamo
il nome della classe come primo parametro (il solo parametro, se non
viene fornito alcun argomento). Possiamo dunque riscrivere il parlare
della Pecora
come:
sub Pecora::parla { my $classe = shift; print "una $classe fa beeeeh!\n"; }
Gli altri due animali risultano simili:
sub Mucca::parla { my $classe = shift; print "una $classe fa muuuu!\n"; } sub Cavallo::parla { my $classe = shift; print "un $classe fa hiiii!\n"; }
In ogni caso, $classe
prenderà il valore appropriato per quella
subroutine. Ma di nuovo, abbiamo un mucchio di struttura simile.
Possiamo raccogliere a fattor comune quello che ancora resta?
Sì, chiamando un ulteriore metodo nella
stessa classe.
Richiamiamo da parla
un metodo di supporto
che chiamiamo suono
. Questo metodo fornisce una costante
di testo per il suono stesso.
{ package Mucca; sub suono { "muuu" } sub parla { my $classe = shift; print "una $classe fa ", $classe->suono, "!\n" } }
Ora, quando chiamiamo Mucca->parla
, riceviamo una $classe
Mucca
in parla
. Questo a sua volta seleziona il metodo
Mucca->suono
che restituisce "muuuu"
. Ma questo, quanto sarà
differente per il Cavallo
?
{ package Cavallo; sub suono { "hiiii" } sub parla { my $classe = shift; print "un $classe fa ", $classe->suono, "!\n" } }
Cambiano solo il nome del package, l'articolo e il suono.
Allora in quale maniera possiamo condividere la definizione di parla
tra la Mucca
e il Cavallo
? Con l'ereditarietà!
Definiamo un package di subroutine comuni chiamato Animale
, con la
definizione per parla
:
{ package Animale; sub parla { my $classe = shift; print $classe->articolo," $classe fa ", $classe->suono, "!\n" } }
Poi, per ogni animale, diciamo che esso ``eredita'' da Animale
,
e definiamo lo specifico suono e l'articolo per quell'animale:
{ package Mucca; @ISA = qw(Animale); sub suono { "muuuu" } sub articolo { "una" } }
Da notare l'aggiunta dell'array @ISA
. Ci arriveremo tra un minuto.
Ma cosa succede quando ora invochiamo Mucca->parla
?
Per prima cosa, Perl costruisce la lista degli argomenti. In questo
caso c'è solo Mucca
. Poi Perl cerca Mucca::parla
. Ma qui non
c'è, così Perl controlla l'array di ereditarietà
@Mucca::ISA
. Questo c'è e contiene il solo nome Animale
.
Quindi, Perl controlla parla
all'interno di Animale
, ovvero
Animale::parla
. E questo c'è, per cui Perl invoca la subroutine con la
lista di argomenti fissata precedentemente.
All'interno della subroutine Animale::parla
, $classe
diventa
Mucca
(che è il primo argomento). Così quando arriva il momento di
invocare $classe->suono
, si cercherà Mucca->suono
,
che si ottiene al primo tentativo senza esaminare @ISA
. Vittoria!
Questa magica variabile chiamata @ISA
(da ``is a''
[è un/una, NdT]), ha dichiarato che Mucca
``è un'' Animale
. Da notare che questa è un array,
non un singolo valore, visto che in rare occasioni ha senso cercare i
metodi mancanti in più di una classe madre.
Se anche Animale
ha una sua @ISA
, allora si cercherà
anche lì. La ricerca è ricorsiva, in
profondità [depth-first, NdT] e da sinistra a destra per
ciascun @ISA
. Di solito, ogni @ISA
possiede un solo elemento
(avere più di un elemento significa ereditarietà
multipla e multipli mal di testa), dunque abbiamo un simpatico albero
di ereditarietà.
Quando utilizziamo use strict
, avremo delle lamentele su @ISA
da parte sua,
dato che non è una variabile contenente un nome di package esplicito,
né è una variabile lessicale (dichiarata con my
). Non possiamo
comunque farne una variabile lessicale (deve appartenere al package per essere trovata
dal meccanismo di ereditarietà), ma il problema si può risolvere in
maniera comoda, in un paio di modi.
La maniera più semplice è quella di specificare il nome del package:
@Mucca::ISA = qw(Animale);
Altrimenti possiamo dichiararla in modo da tenere il nome del package implicito:
package Mucca; use vars qw(@ISA); @ISA = qw(Animale);
Se si sta caricando la classe dall'esterno, attraverso un modulo orientato agli oggetti, si può sostituire:
package Mucca; use Animale; use vars qw(@ISA); @ISA = qw(Animale);
con:
package Mucca; use base qw(Animale);
E questo è proprio compatto.
Aggiungiamo un Topo, che si sente appena:
# Il package Animale e` come prima { package Topo; @ISA = qw(Animale); sub suono { "squit" } sub articolo { "un" } sub parla { my $classe = shift; print $classe->articolo," $classe fa ", $classe->suono, "!\n"; print "[ma si sente appena!]\n"; } }
Topo->parla;
che produce:
un Topo fa squit! [ma si sente appena!]
In questo caso, Topo
ha la sua personale routine per parlare,
dunque Topo->parla
non invoca immediatamente Animale->parla
.
Tutto ciò prende il nome di ``ridefinizione'' [``override''
in Inglese, NdT]. In effetti, non avevamo neppure bisogno di affermare che
un Topo
sia un Animale
, dato che tutti i metodi che sono
necessari a parla
sono completamente definiti in Topo
.
Ma ora abbiamo duplicato parte del codice di Animale->parla
,
e questo può portare ancora una volta ad un mal di testa per la
manutenzione del codice. Come possiamo evitarlo? Possiamo dire in qualche
modo che un Topo
fa tutto quello che fa ogni altro Animale
, ma
aggiungendo il commento in più? Sicuro!
Per prima cosa, possiamo invocare direttamente il metodo Animale::parla
:
# Il package Animale e` come prima { package Topo; @ISA = qw(Animale); sub suono { "squit" } sub articolo { "un" } sub parla { my $classe = shift; Animale::parla($classe); print "[ma si sente appena!]\n"; } }
Notate che ora dobbiamo includere il parametro $classe
(che quasi
certamente ha il valore "Topo"
) come primo parametro per
Animale::parla
, dal momento che non usiamo più la
freccia. Perché non la usiamo? Vediamo, se qui invochiamo Animale->parla
,
il primo parametro per il metodo sarà
"Animale"
e non "Topo"
, e quando sarà il momento di
chiamare il suono
, non si avrà la giusta classe per
ritornare a questo package.
Resta il fatto che invocare direttamente Animale::parla
è un pasticcio.
Cosa succederebbe se Animale::parla
non esistesse, ma fosse
ereditato da una classe menzionata in @Animale::ISA
? Visto che non
stiamo più usando l'invocazione a freccia, abbiamo una ed una sola possibilità
di trovare la giusta subroutine.
C'è anche da notare che il nome della classe Animale
è ora cablato
nella subroutine. Questo è un pasticcio se qualcuno fa
manutenzione del codice, cambiando @ISA
per Topo
senza accorgersi di quel
Animale
in parla
. Quindi è probabile che questa non sia la
maniera corretta di agire.
Una soluzione migliore è quella di dire a Perl di cominciare a cercare da un punto più in alto nella catena di ereditarietà:
# Animale e` come prima { package Topo; # stessi @ISA, &suono e &articolo di prima sub parla { my $classe = shift; $classe->Animale::parla; print "[ma si sente appena!]\n"; } }
Ahh. Questo funziona. Utilizzando questa sintassi, partiamo con Animale
per trovare parla
, ed utilizziamo tutta la catena di ereditarietà di
Animale
se non la trovassimo immediatamente. Inoltre, il primo parametro
sarà $classe
, dunque il metodo parla
trovato avrà Topo
come suo
primo parametro, e riuscirà a trovare Topo::suono
per i dettagli.
Ma questa non è la soluzione ottima. Dobbiamo ancora mantenere coordinati
@ISA
e il package da cui iniziare la ricerca. E peggio ancora, se Topo
avesse più elementi in @ISA
, potremmo non sapere quale
di questi ha effettivamente definito parla
. Ma allora, c'è una soluzione
ancora migliore?
Cambiando la classe Animale
nella classe SUPER
in quella invocazione,
abbiamo una ricerca automatica di tutte le nostre classi madri presenti
in @ISA
:
# Animale e` come prima { package Topo; # stessi @ISA, &suono e &articolo di prima sub parla { my $classe = shift; $classe->SUPER::parla; print "[ma si sente appena!]\n"; } }
Dunque, SUPER::parla
significa cercare parla
nell'@ISA
del package
corrente, invocando il primo trovato. Va notato che non guarda nell'@ISA
di $classe
.
Fino a qui, abbiamo visto la sintassi a freccia per i metodi:
Classe->metodo(@argomenti);
o in maniera analoga:
$a = "Classe"; $a->metodo(@argomenti);
che costruisce una lista di argomenti formata da:
("Classe", @argomenti)
e cerca di invocare
Classe::metodo("Classe", @Argomenti);
Ad ogni modo, se Classe::metodo
non viene trovato, allora @Classe::ISA
viene esaminato (ricorsivamente) per individuare un package che effettivamente contenga
metodo
, e quella subrotuine viene invocata al suo posto.
Utilizzando questa semplice sintassi, abbiamo i metodi di classe, l'ereditarietà (multipla), la ridefinizione e l'estendibilità. Usando quello che abbiamo visto fino a qui, siamo stati in grado di rendere a fattor comune il codice simile e di fornire una maniera carina per riutilizzare le implementazioni con delle variazioni. Questo fa parte del nucleo di quello che possono offrirci gli oggetti, ma essi ci forniscono anche i dati di istanza, che non abbiamo ancora iniziato a vedere.
Partiamo con il codice per la classe Animale
e per la classe
Cavallo
:
{ package Animale; sub parla { my $classe = shift; print $classe->articolo," $classe fa ", $classe->suono, "!\n" } } { package Cavallo; @ISA = qw(Animale); sub suono { "hiiii!" } sub articolo { "un" } }
Questo ci permette di invocare Cavallo->parla
, che risale fino a
Animale::parla
, riscende per chiamare Cavallo::suono
e Cavallo::articolo
per ottenere il
suono e l'articolo giusto, ottenendo infine l'output seguente:
un Cavallo fa hiiii!
Ma tutti i nostri oggetti Cavallo devono essere assolutamente identici. Se viene aggiunta una subroutine, tutti i cavalli automaticamente la condividono. Questo è ottimo se vogliamo che tutti i cavalli siano lo stesso, ma come catturiamo la distinzione tra un singolo cavallo ed un altro? Per esempio supponiamo che io voglia dare un nome al mio primo cavallo. Ci deve essere un modo per mantenere il suo nome separato dagli altri cavalli.
Possiamo fare questo tracciando una nuova distinzione, chiamata una ``istanza''. Una ``istanza'' di solito è creata da una classe. In Perl, ogni riferimento può essere un'istanza, dunque partiamo con il riferimento più semplice che possa contenere il nome di un cavallo: un riferimento ad uno scalare.
my $nome = "Sig. Ed"; my $parlante = \$nome;
Perciò ora $parlante
è un riferimento a quello che sarà il dato specifico
dell'istanza (il nome). Il passo finale per renderlo una vera istanza consiste
nell'usare lo speciale operatore chiamato bless
:
bless $parlante, Cavallo;
Questo operatore immagazzina l'informazione sul package chiamato Cavallo
nella cosa puntata dal riferimento. A questo punto, diciamo che
$parlante
è una istanza di Cavallo
. Ovvero, è uno specifico cavallo.
Nessun altro aspetto del riferimento è cambiato, e può ancora essere usato con i
tradizionali operatori di derefenziazione.
L'invocazione a freccia può essere usata con le istanze, così come con i nomi
dei package (classi). Dunque prendiamo il suono che produce $parlante
:
my $rumore = $parlante->suono;
Per invocare suono
, il Perl per prima cosa si accorge che $parlante
è un riferimento blessed (e quindi una istanza). Poi costruisce una lista
di argomenti, in questo caso formata dal solo $parlante
. (Più avanti
vedremo che gli argomenti si posizioneranno di seguito alla variabile
istanza, proprio come con le classi).
Adesso viene la parte divertente: Perl prende la classe nella quale
l'istanza è stata blessed, in questo caso Cavallo
, e la usa per
individuare la subroutine per invocare il metodo. In questo caso,
Cavallo::suono
viene trovato direttamente (senza l'uso dell'ereditarietà),
producendo l'invocazione finale della subroutine:
Cavallo::suono($parlante)
Da notare che in questo caso il primo parametro è ancora l'istanza e non
il nome della classe come era prima. Avremo "hiiii"
come valore di ritorno,
che finirà nella variable $rumore
.
Se Cavallo::suono
non fosse stato trovato, si sarebbe scorsa tutta la lista di
elementi di @Cavallo::ISA
per cercare di trovare il metodo in una delle
superclassi, analogamente a un metodo di classe.
La sola differenza tra un metodo di classe e un metodo di istanza sta nel primo parametro, che può
essere un'istanza (un riferimento blessed) o un nome di classe (una stringa).
Dato che riceviamo l'istanza come primo parametro, possiamo ora accedere ai suoi dati specifici. In questo caso, andiamo ad aggiungere una maniera per ottenere il nome:
{ package Cavallo; @ISA = qw(Animale); sub suono { "hiiii" } sub articolo { "un" } sub nome { my $self = shift; $$self; } }
Ora chiamiamo per ottenere il nome:
print $parlante->nome, " dice ", $parlante->suono, "\n";
All'interno di Cavallo::nome
, l'array @_
contiene solo $parlante
,
che shift
scrive in $self
. (È consuetudine
trasferire il primo parametro in una variabile chiamata $self
per i
metodi di istanza, perciò fate altrettanto a meno che
non abbiate ottime ragioni).
Alla riga successiva, $self
viene dereferenziata come un riferimento a scalare,
producendo Sig. Ed
, e con questo siamo a posto. Il risultato è:
Sig. Ed dice hiiii.
Naturalmente, se costruiamo tutti i nostri cavalli a mano, molto
probabilmente faremo degli errori di tanto in tanto.
Inoltre, stiamo violando una delle proprietà della programmazione
orientata agli oggetti, in quanto le ``interiora'' di un Cavallo
sono visibili.
Questo è positivo se voi siete dei veterinari, ma non se vi interessa
solo possedere un cavallo. Lasciamo allora che la classe Cavallo
costruisca un nuovo cavallo:
{ package Cavallo; @ISA = qw(Animale); sub suono { "hiiii" } sub articolo { "un" } sub nome { my $self = shift; $$self; } sub chiamato { my $classe = shift; my $nome = shift; bless \$nome, $classe; } }
Ora con il nuovo metodo chiamato
, possiamo costruire un cavallo:
my $parlante = Cavallo->chiamato("Sig. Ed");
C'è da notare che siamo tornati ad un metodo di classe, dunque i
due argomenti di Cavallo::chiamato
sono Cavallo
e "Sig. Ed"
.
L'operatore bless
non solo fa un bless su $nome
, ma restituisce
anche il riferimento a $nome
, che è comodo come valore di ritorno.
E questo è come si costruisce un cavallo.
In questo caso, abbiamo chiamato il costruttore chiamato
in maniera
che indichi chiaramente che l'argomento del costruttore è il nome
per questo particolare Cavallo
.
Si possono usare costruttori differenti con nomi differenti per
differenti modi di ``dare vita'' all'oggetto (come ad esempio
registrare il suo pedigree o la data di nascita). Comunque sia,
noterete che molte persone che arrivano al Perl proveniendo da
linguaggi più limitati, utilizzano un unico costruttore chiamato
new
, interpretando in svariati modi gli argomenti di new
.
Ciascuno dei due stili è adeguato, purché si documenti la propria
particolare maniera di dare vita ad un oggetto. (E stavate già > progettando di farlo, vero?)
C'era qualcosa di specifico di Cavallo
in quel metodo? No. Quindi
è anche la stessa ricetta per costruire qualsiasi cosa che eredita da
Animale
, dunque scriviamolo al suo posto:
{ package Animale; sub parla { my $classe = shift; print $classe->articolo," $classe fa ", $classe->suono, "!\n" } sub nome { my $self = shift; $$self; } sub chiamato { my $classe = shift; my $nome = shift; bless \$nome, $classe; } } { package Cavallo; @ISA = qw(Animale); sub suono { "hiiii" } sub articolo { "un" } }
Ahh, ma cosa succede se invochiamo parla
su un'istanza?
my $parlante = Cavallo->chiamato("Sig. Ed"); $parlante->parla;
Ci ritroviamo con un valore di debug:
un Cavallo=SCALAR(0xaca42ac) dice hiiii!
Perché? Perché la routine Animale::parla
si aspetta un nome di classe
come suo primo parametro, non una istanza. Quando l'istanza viene passata,
finiamo per usare un riferimento blessed a scalare come se fosse una stringa, e questo produce
quello che abbiamo appena visto.
Ci serve che il metodo possa distinguere se è stato
chiamato su una classe o su un'istanza. La maniera più diretta è tramite
l'operatore ref
. Questo restituisce una stringa (il nome della classe)
quando viene usato su di un riferimento blessed, e undef
quando
viene usato su di una stringa (come un nome di una classe).
Andiamo a modificare il metodo nome
per notare la differenza:
sub nome { my $cosa = shift; ref $cosa ? $$cosa # e` un'istanza, restituisco il nome : $cosa->articolo()." $cosa senza nome"; # e` una classe }
Qui, l'operatore ?:
è comodo per selezionare o la deferenziazione,
o la stringa derivata. Ora possiamo usare il metodo sia per un'istanza sia per
una classe. Da notare che ho cambiato il nome del primo parametro
in $cosa
per sottolineare che vogliamo sapere cosa è stato passato:
my $parlante = Cavallo->chiamato("Sig. Ed."); print Cavallo->nome, "\n"; # stampa "un Cavallo senza nome\n" print $parlante->nome, "\n"; # stampa "Sig. Ed\n"
Ora correggiamo parla
per sfruttarlo:
sub parla { my $cosa = shift; print $cosa->nome, " fa ", $cosa->suono, "\n"; }
Dato che suono
funziona già sia con una classe che con un'istanza,
siamo a posto!
Addestriamo i nostri animali a mangiare:
{ package Animale; sub chiamato { my $classe = shift; my $nome = shift; bless \$nome, $classe; } sub nome { my $cosa = shift; ref $cosa ? $$cosa # e` un'istanza, restituisco il nome : $cosa->articolo()." $cosa senza nome"; # e` una classe } sub parla { my $cosa = shift; print $cosa->nome, " fa ", $cosa->suono, "\n"; } sub mangia { my $cosa = shift; my $cibo = shift; print $cosa->nome, " mangia $cibo.\n"; } } { package Cavallo; @ISA = qw(Animale); sub suono { "hiiii" } sub articolo { "un" } } { package Pecora; @ISA = qw(Animale); sub sound { "beeee" } sub articolo { "una" } }
Ora proviamoli:
my $parlante = Cavallo->chiamato("Sig. Ed"); $parlante->mangia("fieno"); Pecora->mangia("erba");
che stampa:
Sig. Ed mangia fieno. una Pecora senza nome mangia erba.
Un metodo di istanza con parametri viene invocato con l'istanza seguita dell'elenco dei parametri. Quindi la prima invocazione è come se fosse:
Animale::mangia($parlante, "fieno");
Cosa succede se un'istanza ha bisogno di più dati? Le istanze più interessanti sono fatte da molte voci, ognuna delle quali può essere a sua volta un riferimento o anche un altro oggetto. La maniera più semplice per memorizzarli è spesso all'interno di un hash. Le chiavi dell'hash vengono utilizzate come i nomi delle parti dell'oggetto (spesso chiamate ``variabili di istanza'' o ``variabili membro''), e i corrispondenti valori sono, beh, i valori.
Ma come facciamo stare un cavallo in un hash? Ricordiamoci che un oggetto era un qualsiasi riferimento blessed. Possiamo usare senza problemi un riferimento ad hash invece che un riferimento a scalare, purché tutto ciò che esamina il rifemento venga modificato in conseguenza.
Facciamo una pecora che abbia un nome ed un colore:
my $cattiva = bless { Nome => "Malvagia", Colore => "nero" }, Pecora;
dunque $cattiva->{Nome}
è Malvagia
, e $cattiva->{Colore}
è nero.
Ma noi vogliamo che $cattiva->nome
acceda al nome e fare questo ora non funziona
più dato che si aspetta un riferimento ad uno scalare.
Non c'è da preoccuparsi, visto che è davvero facile correggerlo:
## in Animale sub nome { my $cosa = shift; ref $cosa ? $cosa->{Nome} : $cosa->articolo()." $cosa senza nome"; }
Naturalmente chiamato
costruisce ancora una pecora scalare, dunque
correggiamo pure quello:
## in Animale sub chiamato { my $classe = shift; my $nome = shift; my $self = { Nome => $nome, Colore => $classe->colore_di_default }; bless $self, $classe; }
Cos'è questo colore_di_default
? Beh, se chiamato
riceve solo il nome,
dobbiamo definire un colore, dunque avremo uno specifico colore iniziale per
la classe.
Per una pecora, possiamo definirlo come bianco:
## in Pecora sub colore_di_default { "bianco" }
E poi, per evitare di doverne definire uno per ogni classe aggiuntiva,
definiremo direttamente in Animale
un metodo a mo' di ``rete di sicurezza''
che funga da ``default default'':
## in Animale sub colore_di_default { "marrone" }
Ora, visto che nome
e chiamato
sono gli unici metodi che
esaminano la ``struttura'' dell'oggetto, il resto dei metodi può
rimanere lo stesso, dunque parla
funziona ancora come prima.
Avere tutti i nostri cavalli di colore marrone sarebbe noioso. Aggiungiamo dunque un metodo o due per ottenere e modificare il colore.
## in Animale sub colore { $_[0]->{Colore} } sub cambia_colore { $_[0]->{Colore} = $_[1]; }
Notate il modo alternativo per accedere agli argomenti: viene usato direttamente $_[0]
,
piuttosto che uno shift
. (Questo ci fa risparmiare un po' di tempo per un
qualcosa che può essere invocato di frequente). E ora possiamo
aggiustare il colore al Sig. Ed:
my $parlante = Cavallo->chiamato("Sig. Ed"); $parlante->cambia_colore("bianco-e-nero"); print $parlante->nome, " e` di colore ", $parlante->colore, "\n";
che produce:
Sig. Ed e` di colore bianco-e-nero
Dunque, a questo punto abbiamo metodi di classe, costruttori, metodi di istanza,
dati di istanza ed anche metodi per accedere ai dati. Ma questo è solo l'inzio di
cosa può offrire il Perl. Non abbiamo ancora inziato a parlare
dei metodi di accesso che funzionano sia in lettura sia in scrittura, dei distruttori, della notazione
a oggetto indiretto, delle sotto-classi che aggiungono dati di istanza, dei dati locali alle classi, dell'overloading,
dei test isa
e can
, della classe UNIVERSAL
, e di un sacco di altre cose. Tutto ciò viene descritto dal
resto della documentazione Perl. Ad ogni modo, speriamo che questo documento vi sia servito per prendere il via.
Per ulteriori informazioni, consultate the perlobj manpage (per tutti quei dettagli sugli oggetti in Perl, ora che avete visto le basi), the perltoot manpage (il tutorial per chi conosce già gli oggetti), perltootc (che ha a che fare con i dati delle classi), the perlbot manpage (per qualche altro trucco), e i libri quali l'eccellente Object Oriented Perl di Damian Conway.
Alcuni moduli che possono dimostarsi interessanti sono: Class::Accessor, Class::Class, Class::Contract, Class::Data::Inheritable, Class::MethodMaker e Tie::SecureHash
Copyright (c) 1999, 2000 di Randal L. Schwartz e Stonehenge Consulting Services, Inc. È permessa la distribuzione di questo documento, integro, assieme alla distribuzione Perl, e in accordo con le licenze della distribuzione Perl; i documenti derivati devono includere, intatta, questa nota di copyright.
Parti di questo testo sono tratte dal materiale didattico apparso originariamente nel corso Packages, References, Objects and Modules [Package, Riferimenti, Oggetti e Moduli, NdT] tenuto dagli istruttori di Stonehenge Consulting Services, Inc, e sono usate con l'autorizzazione.
Parti di questo testo sono tratte da materiale apparso originariamente su Linux Magazine e sono usate con l'autorizzazione.
Copyright (c) 1999, 2000 by Randal L. Schwartz and Stonehenge Consulting Services, Inc. Permission is hereby granted to distribute this document intact with the Perl distribution, and in accordance with the licenses of the Perl distribution; derived documents must include this copyright notice intact.
Portions of this text have been derived from Perl Training materials originally appearing in the Packages, References, Objects, and Modules course taught by instructors for Stonehenge Consulting Services, Inc. and used with permission.
Portions of this text have been derived from materials originally appearing in Linux Magazine and used with permission.
La versione su cui si basa questa traduzione è ottenibile con:
perl -MPOD2::IT -e print_pod perlboot
Per maggiori informazioni sul progetto di traduzione in italiano si veda http://pod2it.sourceforge.net/ .
Traduzione a cura di dree.
Revisione a cura di Gianni Ceccarelli e Hakim Cassimally.
NOME |