index | project | pods | responsabili |
NOMEperlboot - Introduzione alla tecnologia Orientata agli Oggetti (titolo originale: Beginner's Object-Oriented Tutorial)
DESCRIZIONESe 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: perlobj, una guida di riferimento sull'utilizzo degli oggetti e perltoot 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 (perlsub), dei riferimenti (perlref e seguenti), e dei package (perlmod), dunque se non la possedete già, conviene che vi familiarizzate con questi argomenti.
Se potessimo parlare con gli animali...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 O no?
Presentiamo la freccia per invocare i metodiPer ora, diciamo che # 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
Invochiamo un cortilePrendiamo 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); foreach $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 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.
I parametri addizionali dell'invocazione di metodoL'invocazione di: Classe->metodo(@argomenti) tenta di invocare la subroutine 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 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,
Chiamare un secondo metodo per semplificare le coseRichiamiamo da { package Mucca; sub suono { "muuu" } sub parla { my $classe = shift; print "una $classe fa ", $classe->suono, "!\n" } } Ora, quando chiamiamo { 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
Ereditare le tracheeDefiniamo un package di subroutine comuni chiamato { package Animale; sub parla { my $classe = shift; print $classe->articolo," $classe fa ", $classe->suono, "!\n" } } Poi, per ogni animale, diciamo che esso ``eredita'' da { package Mucca; @ISA = qw(Animale); sub suono { "muuuu" } sub articolo { "una" } } Da notare l'aggiunta dell'array Ma cosa succede quando ora invochiamo Per prima cosa, Perl costruisce la lista degli argomenti. In questo
caso c'è solo Quindi, Perl controlla All'interno della subroutine
Qualche nota su @ISAQuesta magica variabile chiamata Se anche Quando utilizziamo 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.
Ridefinire i metodiAggiungiamo 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, Ma ora abbiamo duplicato parte del codice di Per prima cosa, possiamo invocare direttamente il metodo # 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 Resta il fatto che invocare direttamente C'è anche da notare che il nome della classe
Iniziare la ricerca da un posto diversoUna 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 Ma questa non è la soluzione ottima. Dobbiamo ancora mantenere coordinati
Il modo SUPER di fare le coseCambiando la classe # 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,
A che punto siamo arrivati...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 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.
Un cavallo è un cavallo, senza inganno senza inganno -- o no?Partiamo con il codice per la classe { 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 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 bless $parlante, Cavallo; Questo operatore immagazzina l'informazione sul package chiamato
Invocare un metodo di istanzaL'invocazione a freccia può essere usata con le istanze, così come con i nomi
dei package (classi). Dunque prendiamo il suono che produce my $rumore = $parlante->suono; Per invocare Adesso viene la parte divertente: Perl prende la classe nella quale
l'istanza è stata blessed, in questo caso 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 Se
Accesso ai dati dell'istanzaDato 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 Sig. Ed dice hiiii.
Come costruire un cavalloNaturalmente, 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 { 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 my $parlante = Cavallo->chiamato("Sig. Ed"); C'è da notare che siamo tornati ad un metodo di classe, dunque i
due argomenti di In questo caso, abbiamo chiamato il costruttore
Ereditare il costruttoreC'era qualcosa di specifico di { 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 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
Fare un metodo che funzioni sia con le classi che con le istanzeCi serve che il metodo possa distinguere se è stato
chiamato su una classe o su un'istanza. La maniera più diretta è tramite
l'operatore 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 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 sub parla { my $cosa = shift; print $cosa->nome, " fa ", $cosa->suono, "\n"; } Dato che
Aggiungere parametri ad un metodoAddestriamo 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 suono { "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");
Istanze più interessantiCosa 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 ## in Animale sub nome { my $cosa = shift; ref $cosa ? $cosa->{Nome} : $cosa->articolo()." $cosa senza nome"; } Naturalmente ## in Animale sub chiamato { my $classe = shift; my $nome = shift; my $self = { Nome => $nome, Colore => $classe->colore_di_default }; bless $self, $classe; } Cos'è questo ## in Pecora sub colore_di_default { "bianco" } E poi, per evitare di doverne definire uno per ogni classe aggiuntiva,
definiremo direttamente in ## in Animale sub colore_di_default { "marrone" } Ora, visto che
Un cavallo di un altro coloreAvere 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 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
SommarioDunque, 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
ULTERIORI INFORMAZIONIPer ulteriori informazioni, consultate perlobj (per tutti quei dettagli sugli oggetti in Perl, ora che avete visto le basi), perltoot (il tutorial per chi conosce già gli oggetti), perltootc (che ha a che fare con i dati delle classi), perlbot (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
COPYRIGHTCopyright (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.
TRADUZIONE
VersioneLa 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/ .
TraduttoreTraduzione a cura di dree.
RevisoreRevisione a cura di Gianni Ceccarelli e Hakim Cassimally. Mon Jun 11 22:02:13 2012 |