![]() |
index | project | pods | responsabili |
NOMEperlobj - Guida di riferimento agli oggetti in Perl
DESCRIZIONEQuesto documento fornisce una guida di riferimento per le caratteristiche del Perl nell'orientamento agli oggetti. Se state cercando un'introduzione alla programmazione orientata agli oggetti in Perl, si veda perlootut. Al fine di comprendere gli oggetti in Perl, è necessario prima capire i riferimenti in Perl. Per i dettagli si veda perlref. Questo documento descrive tutte le caratteristiche orientate agli oggetti (OO) del Perl, proprio dalle basi. Se state solo cercando di scrivere un po' di codice orientato agli oggetti, vi sarà più utile utilizzare uno dei sistemi ad oggetti che si trovano su CPAN, descritti in perlootut. Se state cercando di scrivere il vostro sistema ad oggetti, oppure è necessario manutenere del codice che implementa gli oggetti da zero allora questo documento vi aiuterà a capire esattamente come funziona il Perl orientato agli oggetti. Ci sono alcuni principi fondamentali che definiscono il Perl orientato agli oggetti:
Diamo un'occhiata in dettaglio a ciascuno di questi principi.
Un Oggetto è Semplicemente una Struttura DatiA differenza di molti altri linguaggi che supportano l'orientamento agli oggetti, Perl non fornisce una sintassi speciale per la costruzione di un oggetto. Gli oggetti sono solo strutture dati Perl (array, hash, scalari, filehandle, ecc) che sono state esplicitamente associate ad una particolare classe. Tale esplicita associazione viene creata dalla funzione integrata Ecco un semplice costruttore: package File; sub new { my $classe = shift; return bless {}, $classe; } Il nome package File; sub carica { my $classe = shift; return bless {}, $classr; } La moderna convenzione per i moduli OO è di usare sempre Negli esempi precedenti, il codice Si può anche utilizzare una variabile per memorizzare un riferimento alla struttura dati che è stata sottoposta a bless quale nostro oggetto: sub new { my $classe = shift; my $self = {}; bless $self, $classe; return $self; } Una volta che abbiamo sottoposto l'hash a bless, hash a cui si fa riferimente tramite sub new { my $classe = shift; my $self = {}; bless $self, $classe; $self->_inizializza(); return $self; } Poiché l'oggetto è anche un hash, è possibile trattarlo come un hash, utilizzarlo per memorizzare i dati associati con l'oggetto. In genere, il codice all'interno della classe può trattare l'hash come una struttura dati accessibile, mentre il codice al di fuori della classe deve sempre trattare l'oggetto come non trasparente. Ciò è chiamato incapsulamento. Incapsulamento significa che l'utente di un oggetto fa come se non fosse necessario sapere come l'oggetto viene implementato. L'utente chiama semplicemente i metodi documentati dell'oggetto. Si noti, tuttavia, che (a differenza di molti altri linguaggi OO) Perl non garantisce o applica in alcun modo l'incapsulamento. Se si desidera che gli oggetti siano in realtà non trasparenti è necessario provvedere alla cosa da soli. Questo può essere fatto in una varietà di modi, incluso l'utilizzo degli oggetti Inside-Out o moduli da CPAN.
Gli Oggetti Sono Sottoposti A Bless; Non Le VariabiliQuando usiamo la funzione bless su qualcosa, non stiamo sottoponendo a bless la variabile che contiene un riferimento a quella cosa, né stiamo sottoponendo a bless il riferimento che la variabile contiene; stiamo sottoponendo a bless la cosa a cui la variabile fa riferimento (conosciuto anche come il referente). Ciò si dimostra meglio con questo codice: use Scalar::Util 'blessed'; my $pippo = {}; my $pluto = $pippo; bless $pippo, 'Classe'; print blessed( $pluto ); # stampa "Classe" $pluto = "un altro valore"; print blessed( $pluto ); # stampa undef Quando chiamiamo A volte si vedono vecchi libri o della documentazione menzionare ``sottoporre a bless un riferimento'' o descrivere un oggetto come ``riferimento sottoposto a bless'', ma questo è errato. Non è il riferimento che è sottoposto a bless come oggetto: è la cosa a cui il riferimento si riferisce (cioè il referente).
Una Classe è Semplicemente un Package >Perl non fornisce una sintassi speciale per le definizioni di classe. Un package è semplicemente un namespace contenente variabili e subroutine. L'unica differenza è che in una classe, i sottoprogrammi si possono aspettare un riferimento ad un oggetto o il nome di una classe come primo argomento. Ciò è puramente una questione di convenzione, quindi una classe può contenere sia metodi sia subroutine che non operano su un oggetto o una classe. Ogni package contiene uno speciale array chiamato È possibile impostare manualmente Comunque le classi genitore siano impostate, la variabile Tutte le classi ereditano implicitamente dalla classe UNIVERSAL. La classe
UNIVERSAL viene implementata dalle funzioni core [appartenente ai moduli base, presenti da subito
nell'installazione perl, NdT] del Perl, e fornisce diversi metodi predefiniti, come ad esempio Perl fornisce solo metodi ereditati come funzionalità incorporate????. L'attributo dell'ereditarietà viene lasciato da implementare alla classe. Per maggiori dettagli si veda Scrivere Accessor.
Un Metodo è Semplicemente una SubroutinePerl non fornisce una sintassi speciale per la definizione di un metodo. Un
metodo è semplicemente una normale subroutine, e viene dichiarata con Perl fornisce una sintassi speciale per l'invocazione di un metodo, l'operatore
La maggior parte dei metodi che scrivete si aspetta di operare su oggetti: sub salva { my $self = shift; open my $fh, '>', $self->percorso() or die $!; print {$fh} $self->dati() or die $!; close $fh or die $!; }
Invocazione dei Metodi >>Chiamare un metodo su un oggetto viene scritto come Il lato sinistro dell'operatore di invocazione (o freccia) del metodo è l'oggetto (o il nome della classe), e il lato destro è il nome del metodo. my $pod = File->new( 'perlobj.pod', $dati ); $pod->salva(); La sintassi Quando si chiama un metodo, la cosa sul lato sinistro della freccia è
passata come primo argomento al metodo. Ciò significa che quando chiamiamo Proprio come con qualsiasi subroutine Perl, tutti gli argomenti passati in Perl sa a quale package appartiene il metodo guardando il lato sinistro della la freccia. Se la sinistra è un nome di package, Perl cerca il metodo in quel package. Se la sinistra è un oggetto, allora Perl cerca il metodo nel package in cui l'oggetto è stato sottoposto a bless. Se la sinistra non è né il nome del package, né un oggetto, allora la chiamata al metodo causerà un errore, ma per maggiori sfumature si veda la sezione Variazioni sulle Chiamate ai Metodi.
Ereditarietà >Abbiamo già parlato dello speciale array Quando una classe eredita da un'altra classe, i metodi definiti nella classe genitore sono a disposizione per la classe figlio. Se si tenta di chiamare un metodo su un oggetto che non è definito nella propria classe, Perl cercherà quel metodo anche in tutte le classi genitore che può avere. package File::MP3; use parent 'File'; # imposta @File::MP3::ISA = ('File'); my $mp3 = File::MP3->new( 'Andvari.mp3', $dati ); $mp3->salva(); Dal momento che non abbiamo definito un metodo In questo caso, Perl trova un metodo Si può effettuare un override [ridefinizione, NdT] del metodo di un genitore in una classe figlia.
Quando lo facciamo, possiamo ancora chiamare il metodo della classe genitore con la pseudo-classe
sub salva { my $self = shift; say 'Preparatevi al rock'; $self->SUPER::salva(); } Il modificatore SUPER::salva($cosa); # INSUCCESSO: cerca la sub salva() nel package SUPER SUPER->salva($cosa); # INSUCCESSO: cerca il metodo salva() nella classe # SUPER $cosa->SUPER::salva(); # Okay: cerca il metodo salva() nella classe # genitore
Come viene Risolto SUPERLa pseudo-classe package A; sub new { return bless {}, shift; } sub parla { my $self = shift; $self->SUPER::parla(); say 'A'; } package B; use parent 'A'; sub parla { my $self = shift; $self->SUPER::parla(); say 'B'; } package C; use parent 'B'; sub parla { my $self = shift; $self->SUPER::parla(); say 'C'; } my $c = C->new(); $c->parla(); In questo esempio, otterremo il seguente output: A B C Ci sono rari casi in cui questa risoluzione ``basata sul package'' può essere un
problema. Se si copia una subroutine da un package ad un altro, la risoluzione di
Ereditarietà multipla multipla>L'ereditarietà multipla denota spesso un problema di progettazione, ma Perl vi dà sempre abbastanza corda per impiccarsi se gliela chiedete. Per dichiarare classi genitori multiple, è sufficiente passare i nomi delle classi multiple
a package PluriFiglio; use parent 'Genitore1', 'Genitore2';
Ordine del Metodo di RisoluzioneL'ordine del metodo di risoluzione conta solo nel caso di ereditarietà multipla. Nel caso di ereditarietà singola, Perl cerca semplicemente lungo la catena di ereditarietà per trovare un metodo: Progenitore | Genitore | Figlio Se chiamiamo un metodo nell'oggetto di un Se Perl non riesce a trovare il metodo in una di queste classi, terminerà con un messaggio di errore. Quando una classe ha più genitori, l'ordine di ricerca del metodo diventa più complicato. Di default, per trovare un metodo Perl effettua una ricerca ``depth-first left-to-right''
[prima in profondità, da sinistra a destra, NdT].
Ciò significa che inizia con il primo genitore nell'array BisnonnoCondiviso / \ NonnoPaterno NonnoMaterno \ / Padre Madre \ / Figlio Quindi, dato il diagramma qui sopra, Perl cercherà è possibile richiedere un differente ordine di risoluzione con la direttiva mro. package Figlio; use mro 'c3'; use parent 'Padre', 'Madre'; Questa direttiva consente di passare all'ordine di risoluzione ``C3''.
In parole povere, l'ordine ``C3'' garantisce che le classi genitore condivise non vengano mai
ricercate prima delle classi figlio, in modo che ora Perl cercherà: L'ordine C3 permette anche di chiamare i metodi in classi di pari livello (sorelle) con la
pseudo-classe
Metodo di Risoluzione della CacheQuando Perl cerca un metodo, inserisce la ricerca nella cache, in modo che le future chiamate al metodo non necessitino di una nuova ricerca. Cambiare la classe genitore di una classe oppure aggiungere subroutine ad una classe, invalida la cache per quella classe. La direttiva mro fornisce alcune funzioni per manipolare direttamente il metodo di risoluzione della cache.
Scrivere CostruttoriCome abbiamo accennato in precedenza, Perl non fornisce alcuna sintassi speciale per scrivere un costruttore. Ciò significa che una classe deve implementare il suo proprio costruttore. Un costruttore è semplicemente un metodo della classe che restituisce un riferimento ad un nuovo oggetto. Il costruttore può anche accettare parametri aggiuntivi che definiscono l'oggetto.
Scriviamo un vero costruttore per la classe package File; sub new { my $classe = shift; my ( $percorso, $dati ) = @_; my $self = bless { percorso => $percorso, dati => $dati, }, $classe; return $self; } Come potete vedere, abbiamo memorizzato il percorso e i dati del file nell'oggetto stesso. Ricordate, dietro le quinte questo oggetto è ancora solo un hash. Più tardi, scriveremo gli accessor per manipolare questi dati. Per la nostra classe File::MP3, possiamo controllare di essere certi che il percorso che gli stiamo fornendo finisca con ``.mp3'': package File::MP3; sub new { my $classe = shift; my ( $percorso, $dati ) = @_; die "Non e` possibile creare un file File::MP3 senza l'estensione mp3\n" unless $percorso =~ /\.mp3\z/; return $classe->SUPER::new(@_); } Questo costruttore consente alla sua classe genitore di effettuare l'effettiva costruzione dell'oggetto.
AttributiUn attributo è un insieme di dati appartenente ad un particolare oggetto. A differenza di molti linguaggi orientati agli oggetti, Perl non fornisce alcuna speciale sintassi o supporto per la dichiarazione e la manipolazione di attributi. Gli attributi sono spesso memorizzati nell'oggetto stesso. Ad esempio, se l'oggetto è un hash anonimo, siamo in grado di memorizzare i valori degli attributi dell'hash utilizzando il nome dell'attributo come chiave. Sebbene sia possibile fare riferimento direttamente a queste chiavi dell'hash al di fuori della classe, è considerata buona norma mascherare tutti gli accessi agli attributi mediante i metodi accessor. Ciò ha diversi vantaggi. Gli accessor rendono più facile cambiare l'implementazione di un oggetto successivamente, pur conservando le API originali. Un accessor consente di aggiungere codice aggiuntivo attorno all'accesso dell'attributo. Per esempio, è possibile applicare un valore di default ad un attributo che non è stato impostato nel costruttore, oppure si potrebbe verificare che sia accettabile un nuovo valore per l'attributo. Infine, utilizzando gli accessor, si rende l'ereditarietà molto più semplice. Le sottoclassi possono utilizzare gli accessor piuttosto che dover conoscere come è implementata internamente una classe genitore.
Scrivere gli AccessorCome con i costruttori, Perl non fornisce alcuna dichiarazione speciale per la sinstassi degli accessor, quindi le classi devono fornire espressamente metodi accessor già scritti????. Ci sono due tipi comuni di accessor, di sola lettura e di lettura-scrittura. Un semplice accessor di sola lettura ottiene semplicemente il valore di un singolo attributo: sub percorso { my $self = shift; return $self->{percorso}; } Un accessor di lettura-scrittura consentirà al chiamante di impostare il valore così come l'ha ottenuto: sub percorso { my $self = shift; if (@_) { $self->{percorso} = shift; } return $self->{percorso}; }
Un Codice Intelligente e Sicuro da Mettere da Parte????Il nostro costruttore e gli accessor non sono molto intelligenti. Non controllano che
un Fare questi controlli a mano può diventare rapidamente noioso. Anche scrivere un gruppo di accessor a mano è incredibilmente noioso. Ci sono molti moduli su CPAN che vi possono aiutare a scrivere codice più sicuro e più conciso, compresi i moduli raccomandati in perlootut.
Variazioni sulle Chiamate ai MetodiPerl supporta diversi altri modi per chiamare i metodi oltre all'utilizo di
Nomi di Metodi come StringhePerl consente di utilizzare una variabile scalare contenente una stringa come nome di un metodo: my $file = File->new( $percorso, $dati ); my $metodo = 'salva'; $file->$metodo(); Questo funziona esattamente come chiamare
Nomi di Classi come StringhePerl consente anche di utilizzare uno scalare contenente una stringa come un nome di una classe: my $classe = 'File'; my $file = $classe->new( $percorso, $dati ); Ancora una volta, questo permette la scrittura di codice molto dinamico.
Riferimenti a Subroutine come MetodiÈ inoltre possibile utilizzare un riferimento a subroutine come un metodo: my $sub = sub { my $self = shift; $self->salva(); }; $file->$sub(); Questo è esattamente equivalente a scrivere if ( my $metodo = $oggetto->can('pippo') ) { $oggetto->$metodo(); }
Dereferenziare le Chiamate ai MetodiPerl consente anche di utilizzare un riferimento ad uno scalare dereferenziato in una chiamata ad un metodo. È impronunciabile, quindi diamo un'occhiata ad un po' di codice: $file->${ \'salva' }; $file->${ restituisce_riferimento_scalare() }; $file->${ \( restituisce_scalare() ) }; $file->${ restituisce_riferimento_a_sub() }; Questo funziona se la dereferenziazione produce una stringa o un riferimento a subroutine.
Chiamate a Metodi su FilehandleDietro le quinte, i filehandle in Perl sono istanze delle classi open my $fh, '>', 'percorso/del/file'; $fh->autoflush(); $fh->print('contenuto'); STDOUT->autoflush();
Invocare i Metodi della ClasseVisto che Perl permette di utilizzare bareword [Letteralmente parola nuda, indica una parola che potrebbe
essere la chiamata di una funzione, ma non avendo né & all'inizio né () alla fine è
per questo ambigua per perl a tempo di compilazione, NdT] per i nomi dei package e nomi di subroutine, a volte
interpreta in maniera erronea il significato di una bareword. Ad esempio, il costrutto È possibile forzare Perl ad usare la prima interpretazione (cioè come un metodo
chiamato nella classe denominata ``Classe'') in due modi. In primo luogo, è possibile aggiungere un
Class::->new() Perl interpreterà questo sempre come una chiamata ad un metodo. In alternativa, si può citare il nome della classe: 'Class'->new() Naturalmente, se il nome della classe è in uno scalare, Perl farà la cosa giusta: my $classe = 'Class'; $classe->new();
La Sintassi degli Oggetti IndirettiAl di fuori del caso dei filehandle, l'uso di questa sintassi è sconsigliato, dato che può confondere l'interprete Perl. Per maggiori dettagli si veda più sotto. Perl supporta un'altra sintassi per l'invocazione dei metodi chiamata notazione dell'``oggetto indiretto''. Questa sintassi si chiama ``indiretta'' perché il metodo è disponibile prima che l'oggetto abbia ricevuto l'invocazione da quel metodo????. Questa sintassi può essere utilizzata con qualsiasi classe o metodo di un oggetto: my $file = new File $percorso, $dati; salva $file; Si consiglia di evitare questa sintassi, per diversi motivi. In primo luogo, può essere fonte di confusione quando la si legge. Nell'esempio precedente, non è
chiaro se Quando viene utilizzato con i metodi della classe, il problema è anche peggiore. Visto che Perl
permette ai nomi di subroutine di essere scritti come bareword, Perl deve indovinare se la bareword dopo il
metodo è un nome di una classe o il nome di una subroutine. In altre parole, Perl
è in grado di risolvere la sintassi sia come Per fare il parsing di questo codice, Perl utilizza un algoritmo euristico basato su quali nomi di package ha visto, su quali subroutine esistono nel package corrente, su quali bareword sono state viste in precedenza e altri input. Inutile dire che, l'euristica può produrre risultati molto sorprendenti! La documentazione più vecchia (ed alcuni moduli CPAN) hanno incoraggiato questa sintassi, in particolare per i costruttori, quindi la si può ancora trovare in codice attualmente in produzione. Tuttavia, vi invitiamo a non utilizzarla in nuovo codice. È possibile forzare Perl a interpretare la bareword come un nome di una classe aggiungendo ad esso ``::'', come abbiamo visto in precedenza: my $file = new File:: $percorso, $dati;
|