![]() |
| index | project | pods | responsabili |
NAMEperlobj - Gli oggetti in Perl
DESCRIPTIONPer prima cosa, avete bisogno di capire che cosa sono i referimenti in Perl.
Consultate perlref per questo. In secondo luogo, se trovate ancora troppo
complicato il Se siete ancora con noi (? se ci state ancora seguendo), allora qui ci sono tre semplici definizioni che dovreste trovare rassicuranti.
Ora tratteremo esaurientemente questi punti in maggiore profondità.
Un Oggetto è Semplicemente un RiferimentoDiversamente da ciò che accade in C++, Perl non fornisce una sintassi speciale per i costruttori. Un costruttore è solamente una subroutine che restituisce un riferimento a qualcosa di ``blessed'' [letteralmente ``benedetto'', si intende sottoposto alla funzione bless, N.d.T.] nella classe, generalmente la classe nella quale la subroutine è definita. Questo è un tipico costruttore:
package Critter;
sub new { bless {} }
La parola
package Critter;
sub spawn { bless {} }
Questo potrebbe persino esser preferibile, poiché i programmatori C++
non saranno Una cosa che differisce tra i costruttori in Perl rispetto a
quelli in C++ è, che in Perl, devono allocarsi la propria memoria.
(L'altra cosa è che non chiamano automaticamente i costruttori
della classe base sovrascritta(?)).
sub new {
my $self = {};
bless $self;
return $self;
}
Spesso vedete una cosa come questa in costruttori più complicati che vogliono chiamare metodi nella classe come parte della costruzione:
sub new {
my $self = {};
bless $self;
$self->initialize();
return $self;
}
Se vi preoccupate dell'ereditarietà (e dovreste; si veda perlmodlib/``Modules: Creation, Use, and Abuse'' [``Moduli: Creazione, Uso ed Abuso''>, N.d.T.]) allora potete utilizzare la forma a due argomenti di bless così che i vostri costruttori possano essere ereditati:
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
$self->initialize();
return $self;
}
Oppure se vi aspettate che le persone non chiamino solamente
sub new {
my $this = shift;
my $class = ref($this) || $this;
my $self = {};
bless $self, $class;
$self->initialize();
return $self;
}
Nei package della classe, i metodi comunicheranno tipicamente con i riferimenti come riferimenti classici. Fuori dal package della classe, il riferimento è generalmente trattato come un valore opaco che potrebbe esser acceduto solo attraverso i metodi della classe. Sebbene un costruttore possa in teoria re-bless un oggetto riferito correntemente che appartiene ad un'altra classe, quasi sicuramente ciò vi porterà in confusione. La nuova classe è responsabile per tutte le pulizie successive. Il blessing precedente è dimenticato, come se un oggetto potesse appartenere ad una sola classe alla volta. (Sebbene naturalmente esso sia libero di ereditare metodi da molte classi). Se vi trovate a doverlo fare, la parent class [``classe genitore'', N.d.T.] si sta probabilmente comportando male. Un chiarificazione: gli oggetti in Perl sono sottoposti a bless. I
riferimenti no. Gli oggetti sanno a quale package appartengono. I
riferimenti no. La funzione
$a = {};
$b = $a;
bless $a, BLAH;
print "\$b is a ", ref($b), "\n";
Questo mette in relazione $b come se fosse un BLAH, così facendo
ovviamente
Una classe è Semplicemente un PackageDiversamente dal C++, Perl non fornisce una sintassi speciale per la definizione delle classi. Potete utilizzare un package come una classe mettendo le definizioni dei metodi dentro la classe. C'è un array speciale all'interno di ciascun package chiamato @ISA, il
quale dice dove altro guardare per un metodo se non viene trovato nel package corrente. Questo è il modo in cui Perl implementa
l'ereditarietà. Ciascun elemento dell'array @ISA è solamente il nome di
un altro package che in realtà è un class package. Le classi sono
cercate (prima dal fondo) per metodi non trovati nell'ordine in cui sono
in @ISA. Le classi accessibili attraverso @ISA sono conosciute come
classi base della classe corrente. Tutte le classi implicitamente
ereditano dalla classe Se viene trovato un metodo mancande nella classe base, esso viene posto in cache nella classe corrente per efficienza. Cambiando @ISA o definendo nuove subroutine invalida la cache e costringe Perl ad una nuova ricerca. Se né la classe corrente, la sua classe base con nome, né la classe UNIVERSAL contengono il metodo richiesto, questi tre posti vengono ricontrollati, questa volta guardando per un metodo chiamato AUTOLOAD(). Se viene trovato AUTOLOAD, questo metodo viene chiamato in vece del metodo mancante, impostando la variabile package globale $AUTOLOAD per essere un nome completamente qualificato per il metodo che si intendeva chiamare. Se nulla di questo funziona, Perl finalmente lascia perdere e protesta. Se volete interrompere la catena di ereditarietà di AUTOLOAD mettete semplicemente una sub vuota
sub AUTOLOAD;
e la chiamata originaria fallirà (die) lamentandosi di non aver trovato la sub che cercava. Le classi Perl fanno solamente ereditarietà di metodo. L'ereditarietà dati è lasciata alla classe stessa. Questo non è un problema in Perl, dato che molte classi modellano gli attributi dei loro oggetti utilizzando un hash anonimo, che fa da piccolo namespace proprio per essere spartito dalle varie classi che potrebbero voler far qualcosa cone l'oggetto. Il solo problema con tutto ciò, è che non potete esser sicuri di non stare utilizzando un pezzo di hash che non sia già stato utilizzato. Una soluzione ragionevole è prependere il nome del package al nome del campo considerato nell'hash.
sub bump {
my $self = shift;
$self->{ __PACKAGE__ . ".count"}++;
}
Un Metodo è Semplicemente una SubroutineDiversamente dal C++, Perl non fornisce una sintassi speciale per la definizione dei metodi. (Sebbene esso fornisca una piccola sintassi per l'invocazione dei metodi. La vedremo dopo). Un metodo si aspetta che il suo primo argomento sia un oggetto (riferimento) oppure un package (stringa) sul quale è stato invocato. Ci sono due strade per chiamare i metodi, che chiameremo ``metodi classe'' e ``metodi instanza''. Un metodo classe si aspetta il nome di una classe come suo primo argomento. Esso fornisce funzionalità per la classe nel suo insieme, non per i singoli oggetti appartenenti alla classe. I costruttori sono spesso metodi classe, ma consultate perltoot e perltooc per delle alternative. Molti metodi classe ignorano semplicemente il loro primo argomento, perché essi già conoscono in quale package sono e non si curano del package attraverso il quale sono stati invocati. (Questi non sono necessariamente gli stessi, perché i metodi classe seguono l'albero di ereditarietà giusto come un'istanza ordinaria di un metodo). Un altro tipico utilizzo dei metodi classe è cercare un oggetto attraverso il nome:
sub find {
my ($class, $name) = @_;
$objtable{$name};
}
Un metodo istanza si aspetta un riferimento ad un oggetto come suo primo argomento. Tipicamente esso trasferisce il primo argomento in una variabile ``self'' o ``this'', e dopo la utilizza come un riferimento classico.
sub display {
my $self = shift;
my @keys = @_ ? @_ : sort keys %$self;
foreach $key (@keys) {
print "\t$key => $self->{$key}\n";
}
}
Invocazione del Metodo >>Per varie ragioni storiche e non, Perl offre due strade equivalenti per scrivere una chiamata di metodo. Il modo più semplice e comune è utilizzare la notazione con la freccia:
my $fred = Critter->find("Fred");
$fred->display("Height", "Weight");
Dovreste avere già familiarità con l'utilizzo
dell'operatore Qualsiasi cosa sulla sinistra della freccia, sia un riferimento o un nome di classe, viene passato al metodo come suo primo argomento. Il codice sopra è generalmente equivalente a:
my $fred = Critter::find("Critter", "Fred");
Critter::display($fred, "Height", "Weight");
Come fa a sapere Perl in quale package si trova la subroutine? Guardando al lato sinistro della freccia, il quale deve esser sia un nome di package o un riferimento ad un oggetto, cioè qualcosa che è stato blessed ad un package. In entrambi i casi, questo è il package nel quale Perl comincia a cercare. Se quel package non ha subroutine con quel nome, Perl comincia a cercarlo in qualsiasi classe base di quel package, e così via. Se ne avete bisogno, potete forzare Perl a cominciare la ricerca in qualche altro package:
my $barney = MyCritter->Critter::find("Barney");
$barney->Critter::display("Height", "Weight");
Qui Come caso speciale di quello precedente, potreste utilizzare la
pseudo-classe
package MyCritter;
use base 'Critter'; # imposta @MyCritter::ISA = ('Critter');
sub display {
my ($self, @args) = @_;
$self->SUPER::display("Name", @args);
}
È importante notare che
something->SUPER::method(...); # OK
SUPER::method(...); # SBAGLIATO
SUPER->method(...); # SBAGLIATO
Invece del nome di una classe o del riferimento ad un oggetto, potete anche utilizzare qualunque espressione che restituisce ambedue le parti sul lato sinistro della freccia. Dunque, la seguente istruzione è valida:
Critter->find("Fred")->display("Height", "Weight");
ed anche la seguente seguente:
my $fred = (reverse "rettirC")->find(reverse "derF");
La parte destra della freccia è tipicamente il nome del metodo, ma una variabile scalare semplice che contenga o il nome del motodo o un riferimento a subroutine può anche esser utilizzato
Sintassi indiretta degli OggettiL'altra strada per invocare un metodo è utilizzando la notazione detta ``indirect object''. Questa sintassi era disponibile in Perl 4 prima che gli oggetti fossero introdotti, ed è ancora utilizzata con i filehandles come questo: print STDERR "aiuto!!!\n"; La stessa sintassi può esser utilizzata per chiamare sia un oggetto che un metodo classe my $fred = find Critter "Fred"; display $fred "Height", "Weight"; Notate che non c'è la virgola tra l'oggetto (o il nome della classe) e i parametri. è in questo modo che Perl si accorge che volete una chiamata indiretta a metodo piuttosto che una normale invocazione di subroutine. Ma che cosa accade se non ci sono argomenti? In quel caso Perl deve supporre quello che voi volete. Peggio ancora, deve tirare ad indovinare durante la compilazione. Normalmente Perl ci indovina, ma quando negli altri casi vi ritrovate una chiamata di funzione compilata come invocazione di metodo, o viceversa. Ciò può introdurre dei bug piuttosto difficili da scovare. Per esempio, una chiamata al metodo C'è un altro problema con questa sintassi: l'oggetto indiretto è limitato al nome, ad una variabile scalare, o un blocco, perché altrimenti avrebbe da fare troppe ricerche, così come qualsiasi altro dereferenziamento postfisso nel linguaggio. (Sono le stesse bizarre regole come si usano per i filehandle in funzioni come
move $obj->{FIELD}; # probabilmente sbagliato!
move $ary[$i]; # probabilmente sbagliato!
Vengono attualmente parsate [``analizzate'' N.d.T.] con molta sorpresa:
$obj->move->{FIELD}; # Bene, guarda qui
$ary->move([$i]); # Non vi aspettavate questo, eh?
Anziché quello che avreste previsto:
$obj->{FIELD}->move(); # Dovreste esser cosi` fortunati.
$ary[$i]->move; # Yeah, sicuro.
Per ottenere un comportamente corretto con la sintassi ``indirect object'', dovreste utilizzare un blocco intorno all'oggetto indiretto:
move {$obj->{FIELD}};
move {$ary[$i]};
Addirittura quindi, potreste ancora avere lo stesso potenziale problema se
ci fosse una funzione chiamata
Metodi di default UNIVERSALIl package
Potreste aggiungere altri metodi alla classe UNIVERSAL attraverso Perl o
codice XS. Non dovreste aver bisogno di
DistruttoriQuando l'ultimo riferimento ad un oggetto va via, l'oggetto viene
automaticamente distrutto. (Questo potrebbe accadere dopo la terminazione
del programma, se avete salvato un riferimento in qualche variabile
globale.) Se volete avere il controllo di ciò che accade appena prima che
l'oggetto sia liberato, allora potreste definire un metodo DESTROY
nella vostra classe. Esso sarà chiamato automaticamente al momento
appropriato, e voi potrete fare le ulteriori pulizie di cui avete
bisogno. Perl passa (al metodo DESTROY) un riferimento all'oggetto
che sta per esser distrutto come primo (ed unico) argomento. Siate sicuri
che il riferimento sia un valore read-only [``accedibile in sola lettura''
N.d.T.], e che non possa esser modificato manipolando Dato che i metodi DESTROY possono esser chiamati in tempi non
predicibili a priori, è importante che voi localizziate qualsiasi
variabile globale che il metodo potrebbe aggiornare. In particolare,
localizzate Se vi arrangiate a re-blessare il riferimento prima che il distruttore ritorni, Perl chiamerà ancora il metodo DESTROY per l'oggetto re-blessed dopo che quello corrente ritorna. Questo può esser utilizzato per delegare in modo pulito la distruzione di oggetti, o per assicurarsi che i distruttori nelle classi base di vostra scelta siano chiamati. Chiamare esplicitamente DESTROY è possibile, ma di solito non se ne ha bisogno. Non confondete la discussione precedente con quella sul come gli oggetti CONTAINED siano distrutti nell'oggetto corrente. Tali oggetti saranno liberati e distrutti automaticamente quando l'oggetto corrente viene liberato, non fornendo altri riferimenti a loro esisteranno da qualche altra parte.
SommarioQuesto è tutto. Ora ciò di cui avete bisogno è comprare un libro sulla metodologia di progettazione object-oriented, e far esplodere la vostra testa con esso nei prossimi sei mesi o più.
Garbage Collection a Due FasiPer mosti scopi, Perl utilizza un veloce e semplice, sistema di
garbage collection basato su riferimenti. Questo significa che c'è
un altro dereferenziamento che avviene a qualche livello, così se non
avete compilato il vostro eseguibile Perl utilizzando il flag del
del compilatore C Una faccenda più seria è che la memoria irraggiungibile con un contatore di riferimenti non a zero non verrà liberata normalmente. Quindi questa è una cattiva idea:
{
my $a;
$a = \$a;
}
Anche se $a dovrebbe sparire, non può. Quando avete costruito strutture dati ricorsive, dovrete spezzare i cicli di riferimenti a mano, altrimenti la memoria non sarà liberata. Per esempio, qui c'è un nodo che si auto-referenzia così che uno lo potrebbe utilizzare in una sofisticata struttura ad albero:
sub new_node {
my $class = shift;
my $node = {};
$node->{LEFT} = $node->{RIGHT} = $node;
$node->{DATA} = [ @_ ];
return bless $node => $class;
}
Se create nodi come questi, essi (correntemente) non dovrebbero sparire senza che voi spezziate il loro auto riferimento. (In altro parole, questo non dovrebbe esser interpretato come una caratteristica, e voi non dovreste farvi affidamento). Quasi. Quando un thread dell'interprete finalmente si spegne (solitamente quando il vostro programma esce), allora viene fatto un abbastanza costoso ma completo mark-and-sweep [``segna e pulisci'' N.d.T.] stile di garbage collection, e qualsiasi cosa allocata da quel thread viene distrutta. Questo è essenziale per supportare un linguaggio multithread come Perl. Per esempio, questo programma dimostra la garbage collection a due fasi di Perl:
#!/usr/bin/perl
package Subtle;
sub new {
my $test;
$test = \$test;
warn "CREATING " . \$test;
return bless \$test;
}
sub DESTROY {
my $self = shift;
warn "DESTROYING $self";
}
package main;
warn "starting program";
{
my $a = Subtle->new;
my $b = Subtle->new;
$$a = 0; # break selfref
warn "leaving block";
}
warn "just exited block";
warn "time to die...";
exit;
Quando fate girare /foo/test, viene prodotto il seguente output:
starting program at /foo/test line 18.
CREATING SCALAR(0x8e5b8) at /foo/test line 7.
CREATING SCALAR(0x8e57c) at /foo/test line 7.
leaving block at /foo/test line 23.
DESTROYING Subtle=SCALAR(0x8e5b8) at /foo/test line 13.
just exited block at /foo/test line 26.
time to die... at /foo/test line 27.
DESTROYING Subtle=SCALAR(0x8e57c) during global destruction.
Notate quel ``global destruction''? Quello è il thread della garbage collection che sta raggiungendo l'irraggiungibile. Gli oggetti sono sempre distrutti, anche quando non ci sono riferimenti
regolari. Gli oggetti sono distrutti in passi separati, prima i riferimenti
ordinari così da prevenire che il distruttore di oggetti utilizzi dei
riferimenti che sono stati essistessi già distrutti. I riferimenti normali
sono garbage-collected se il ``destruct level'' è maggiore di 0. Potete
testare i livelli maggiori di ``global destruction'' andando a settare la
variabile d'ambiente PERL_DESTRUCT_LEVEL, presumendo che In futuro sarà implementata una strategia di garbage collection più completa. Nel frattempo, la miglior soluzione è creare delle classi container non ricorsive che mantengano un puntatore alla struttura dati self-referential. Definire un metodo DESTROY per gli oggetti ``containing'' della classe che manualmente andranno a rompere le circolarità nella struttura self-referential.
SI VEDA ANCHEUn tutorial più leggero sulla programmazione orientata agli oggetti in Perl può esser trovato in perltoot, perlboot e perltooc. Dovreste anche controllare perlbot per altri trucchi, trappole e suggerimenti sugli oggetti, così come perlmodlib per delle ragguardevoli linee guida sulla costruzione di moduli e classi.
TRADUZIONE
VersioneLa versione su cui si basa questa traduzione è ottenibile con: perl -MPOD2::IT -e print_pod perlobj Per maggiori informazioni sul progetto di traduzione in italiano si veda http://pod2it.sourceforge.net/ .
TraduttoreTraduzione a cura di Daniele Ludovici.
RevisoreRevisione a cura di . Wed Jan 28 14:17:56 2009 |