=head1 NAME perlbot - La scatola degli attrezzi degli Oggetti =head1 DESCRIZIONE La seguente raccolta di trucchi e suggerimenti E destinata a stimolare la curiositE a proposito di cose come l'utilizzo di variabili di istanza e i meccanismi delle relazioni tra oggetto e classe. Il lettore E incoraggiato a consultare dei testi pertinenti a proposito di definizioni e metodologie per la programmazione OO (orientata agli oggetti). Questo documento non E inteso come una guida per principianti per la programmazione orientata agli oggetti nE come una guida completa alle caratteristiche del Perl orientato agli oggetti, e nemmeno come guida di stile. Se state cercando delle guide per principianti, assicuratevi di leggere L, L e L. Il motto del Perl E ancora valido: ci sono molti modi per fare le cose. =head1 SUGGERIMENTI DI SCALABILITE OO =over 5 =item 1 Non tentate di verificare il tipo di $self. Non funzionerE se la classe E ereditata, quando il tipo di $self E valido ma il suo package non E quello che vi aspettereste. Si veda la regola numero 5. =item 2 Se viene utilizzata una sintassi orientata agli oggetti (OO) o indiretta per gli oggetti (IO), allora l'oggetto E probabilmente del tipo corretto e non ci sono motivi per essere paranoici al riguardo. In ogni caso, Perl non E un linguaggio paranoico. Se qualcuno rovescia la sintassi OO o IO, allora costui probabilmente sa cosa sta facendo e dovreste lasciarlo fare. Si veda la regola numero 1. =item 3 Utilizzate la forma a due argomenti per il bless(). Permettete ad una sottoclasse di utilizzare il vostro costruttore. Si veda L. =item 4 La sottoclasse E autorizzata a sapere cose che riguardano la sua immediata superclasse, la superclasse non E autorizzata a sapere alcunchE di una sottoclasse. =item 5 Non abbiate il grilletto facile con l'ereditarietE. Relazioni di "utilizzo", "contenimento" o "delegazione" (qualche sorta di aggregazione, almeno) sono spesso piE appropriate. Si veda L, L, e L<"DELEGAZIONE">. =item 6 L'oggetto E il namespace. Rendete le variabili globali del package accessibili mediante l'oggetto. Ci toglier il bisogno di tirare a indovinare il package da cui proviene il simbolo. Si veda L. =item 7 La sintassi IO E sicuramente meno vistosa, ma anche incline ad ambiguitE che possono causare errori difficili da trovare. Lasciate che le persone utilizzino la sintassi con la freccia, anche se non vi piace. =item 8 Non utilizzate la sintassi per la chiamata di funzione su un metodo. Un giorno o l'altro sarete nei guai. Qualcuno potrebbe spostare quel metodo in una superclasse ed il vostro codice non funzionerebbe piE. Come se non bastasse, state alimentando la paranoia di cui alla regola 2. =item 9 Non assumete di conoscere il package di base di un metodo. Stareste rendendo difficile, per alcuni, fare la sovrapposizione [override, NdT] di quel metodo. Si veda L. =back =head1 VARIABILI DI ISTANZA Per mantenere le variabili di istanza E possibile utilizzare un array anonimo o un hash anonimo. Vengono mostrati anche dei parametri con nome. package Pippo; sub new { my $tipo = shift; my %parametri = @_; my $self = {}; $self->{'Alto'} = $parametri{'Alto'}; $self->{'Basso'} = $parametri{'Basso'}; bless $self, $tipo; } package Pluto; sub new { my $tipo = shift; my %parametri = @_; my $self = []; $self->[0] = $parametri{'Sinistra'}; $self->[1] = $parametri{'Destra'}; bless $self, $tipo; } package main; $a = Pippo->new( 'Alto' => 42, 'Basso' => 11 ); print "Alto=$a->{'Alto'}\n"; print "Basso=$a->{'Basso'}\n"; $b = Pluto->new( 'Sinistra' => 78, 'Destra' => 40 ); print "Sinistra=$b->[0]\n"; print "Destra=$b->[1]\n"; =head1 VARIABILI SCALARI DI ISTANZA Quando serve solamente una variabile di istanza, puE essere utilizzato uno scalare anonimo. package Pippo; sub new { my $tipo = shift; my $self; $self = shift; bless \$self, $tipo; } package main; $a = Pippo->new( 42 ); print "a=$$a\n"; =head1 EREDITARIETE DI VARIABILI DI ISTANZA Questo esempio mostra come si possano ereditare variabili di istanza da una superclasse, per inclusione nella nuova classe. CiE richiede di chiamare il costruttore della superclasse e di aggiungere le proprie variabili di istanza al nuovo oggetto. package Pluto; sub new { my $tipo = shift; my $self = {}; $self->{'tizio'} = 42; bless $self, $tipo; } package Pippo; @ISA = qw( Pluto ); sub new { my $tipo = shift; my $self = Pluto->new; $self->{'caio'} = 11; bless $self, $tipo; } package main; $a = Pippo->new; print "tizio = ", $a->{'tizio'}, "\n"; print "caio = ", $a->{'caio'}, "\n"; =head1 RELAZIONI TRA OGGETTI Il seguente codice mostra come si possano implementare relazioni di "contenimento" e "utilizzo" tra oggetti. package Pluto; sub new { my $tipo = shift; my $self = {}; $self->{'tizio'} = 42; bless $self, $tipo; } package Pippo; sub new { my $tipo = shift; my $self = {}; $self->{'Pluto'} = Pluto->new; $self->{'caio'} = 11; bless $self, $tipo; } package main; $a = Pippo->new; print "tizio = ", $a->{'Pluto'}->{'tizio'}, "\n"; print "caio = ", $a->{'caio'}, "\n"; =head1 SOVRAPPORRE I METODI DELLA SUPERCLASSE L'esempio seguente mostra come sovrapporre un metodo della superclasse e quindi chiamare il metodo sovrapposto. La pseudo-classe B permette al programmatore di chiamare un metodo sovrapposto di una superclasse senza in realtE sapere dove quel metodo E definito. package Paperino; sub goo { print "ecco il goo\n" } #[sostanza appicicosa, NdT] package Pluto; @ISA = qw( Paperino ); sub google { print "c'e` google\n" } package Topolino; sub rimugina { print "sto rimuginando\n" } package Pippo; @ISA = qw( Pluto Topolino ); sub new { my $tipo = shift; bless [], $tipo; } sub grr { print "sto borbottando\n" } sub goo { my $self = shift; $self->SUPER::goo(); } sub rimugina { my $self = shift; $self->SUPER::rimugina(); } sub google { my $self = shift; $self->SUPER::google(); } package main; $pippo = Pippo->new; $pippo->rimugina; $pippo->grr; $pippo->goo; $pippo->google; Osservate che C si riferisce alla superclasse del package corrente (C), non alla superclasse di C<$self>. =head1 USARE LE RELAZIONI CON SDBM Questo esempio mostra l'interfaccia alla classe SDBM. Essa crea una relazione di "utilizzo" tra la classe SDBM e la nuova classe Miodbm. package Miodbm; require SDBM_File; require Tie::Hash; @ISA = qw( Tie::Hash ); sub TIEHASH { my $tipo = shift; my $ref = SDBM_File->new(@_); bless {'dbm' => $ref}, $tipo; } sub FETCH { my $self = shift; my $ref = $self->{'dbm'}; $ref->FETCH(@_); } sub STORE { my $self = shift; if (defined $_[0]){ my $ref = $self->{'dbm'}; $ref->STORE(@_); } else { die "Impossibile effettuare STORE su una chiave indefinita in Miodbm\n"; } } package main; use Fcntl qw( O_RDWR O_CREAT ); tie %pippo, "Midbm", "Sdbm", O_RDWR|O_CREAT, 0640; $pippo{'pluto'} = 123; print "pippo-pluto = $pippo{'pluto'}\n"; tie %pluto, "Midbm", "Sdbm2", O_RDWR|O_CREAT, 0640; $pluto{'Cathy'} = 456; print "pluto-Cathy = $pluto{'Cathy'}\n"; =head1 PENSARE AL RIUTILIZZO DEL CODICE Una forza dei linguaggi Orientati agli Oggetti E la facilitE con la quale il vecchio codice puE utilizzare il nuovo codice. Il seguente esempio mostrerE prima di tutto come si possa ostacolare il riutilizzo del codice e di seguito come si possa favorire il riutilizzo del codice. Questo primo esempio mostra una classe che utilizza una chiamata al nome del metodo per intero per accedere al metodo privato TOPOLINO(). Il secondo esempio mostrerE che E possibile effettuare una sovrapposizione del metodo TOPOLINO(). package PIPPO; sub new { my $tipo = shift; bless {}, $tipo; } sub pluto { my $self = shift; $self->PIPPO::privato::TOPOLINO; } package PIPPO::privato; sub TOPOLINO { print "in TOPOLINO\n"; } package main; $a = PIPPO->new; $a->pluto; Adesso proviamo ad effettuare una sovrapposizione del metodo TOPOLINO(). Vorremmo che PIPPO::pluto() chiamasse VAIOP::TOPOLINO(), ma ciE non puE accadere perchE PIPPO::pluto() chiama esplicitamente PIPPO::private::TOPOLINO(). package PIPPO; sub new { my $tipo = shift; bless {}, $tipo; } sub pluto { my $self = shift; $self->PIPPO::privato::TOPOLINO; } package PIPPO::privato; sub TOPOLINO { print "in TOPOLINO\n"; } package VAIOP; @ISA = qw( PIPPO ); sub new { my $tipo = shift; bless {}, $tipo; } sub TOPOLINO { print "in VAIOP::TOPOLINO\n"; } package main; $a = VAIOP->new; $a->pluto; Per creare codice riutilizzabile dobbiamo modificare la classe PIPPO, abbattendo la classe PIPPO::private. Il prossimo esempio mostra una classe PIPPO riutilizzabile che permette al metodo VAIOP::TOPOLINO() di essere utilizzato al posto di PIPPO::TOPOLINO(). package PIPPO; sub new { my $tipo = shift; bless {}, $tipo; } sub pluto { my $self = shift; $self->TOPOLINO; } sub TOPOLINO { print "in TOPOLINO\n"; } package VAIOP; @ISA = qw( PIPPO ); sub new { my $tipo = shift; bless {}, $tipo; } sub TOPOLINO { print "in VAIOP::TOPOLINO\n"; } package main; $a = VAIOP->new; $a->pluto; =head1 IL CONTESTO DI CLASSE E L'OGGETTO Utilizzate l'oggetto per risolvere problemi riguardanti il contesto di package e classe. Tutto ciE di cui ha bisogno un metodo dovrebbe essere disponibile attraverso l'oggetto o dovrebbe essere passato come un parametro a quel metodo. Una classe avrE talvolta dati statici o globali per essere utilizzati dai metodi. Una sottoclasse potrebbe voler scavalcare con un override questi dati e rimpiazzarli con nuovi dati. Quando ciE accade, la superclasse puE non sapere come trovare la nuova copia dei dati. Questo problema puE essere risolto utilizzando l'oggetto per definire il contesto del metodo. Permettete al metodo di cercare nell'oggetto un riferimento ai dati. L'alternativa E forzare il metodo per farlo andare a caccia dei dati ("E nella mia classe o in una sottoclasse? Quale sottoclasse?"), e ciE puE non essere conveniente e porterE a scrivere codice strambo. E meglio permettere semplicemente all'oggetto di dire al metodo dove sono localizzati i dati. package Pluto; %fiasco = ( 'Password' => 'XYZZY' ); sub new { my $tipo = shift; my $self = {}; $self->{'fiasco'} = \%fiasco; bless $self, $tipo; } sub enter { my $self = shift; # Non prova a indovinare se debba essere usato %Pluto::fiasco # o %Pippo::fiasco. L'oggetto sa gia` quale si debba usare, # quindi basta chiederlo. # my $fiasco = $self->{'fiasco'}; print "La parola e` ", $fiasco->{'Password'}, "\n"; } package Pippo; @ISA = qw( Pluto ); %fiasco = ( 'Password' => 'Rumple' ); sub new { my $tipo = shift; my $self = Pluto->new; $self->{'fiasco'} = \%fiasco; bless $self, $tipo; } package main; $a = Pluto->new; $b = Pippo->new; $a->enter; $b->enter; =head1 EREDITARE UN COSTRUTTORE Un costruttore ereditabile dovrebbe utilizzare la seconda forma di bless() che premette di "consacrare" direttamente in una classe specifica. Osservate in quest'esempio che l'oggetto sarE un PLUTO e non un PIPPO, anche se il costruttore E nella classe PIPPO. package PIPPO; sub new { my $tipo = shift; my $self = {}; bless $self, $tipo; } sub topolino { print "in PIPPO::topolino()\n"; } package PLUTO; @ISA = qw(PIPPO); sub topolino { print "in PLUTO::topolino()\n"; } package main; $a = PLUTO->new; $a->topolino; =head1 DELEGAZIONE Per alcune classi, tipo la SDBM_File, non si possono creare efficacemente sottoclassi perchE queste creano oggetti esterni. Classi di questo tipo possono essere estese con una specie di tecnica di aggregazione tipo la relazione di "utilizzo" menzionata in precedenza o per delega. Il seguente esempio mostra la delegazione utilizzando una funzione AUTOLOAD() per effettuare l'inoltro dei messaggi. Questo permetterE all'oggetto Miodbm di comportarsi esattamente come un oggetto SDBM_File. Possiamo estendere il comportamento della classe Miodbm aggiungendo metodi custom FETCH() e STORE(), se si desidera. package Miodbm; require SDBM_File; require Tie::Hash; @ISA = qw(Tie::Hash); sub TIEHASH { my $tipo = shift; my $ref = SDBM_File->new(@_); bless {'delega' => $ref}; } sub AUTOLOAD { my $self = shift; # L'interprete Perl posiziona il nome del # messaggio in una variabile chiamata $AUTOLOAD. # I messaggi DESTROY non dovrebbero essere mai propagati. return if $AUTOLOAD =~ /::DESTROY$/; # Rimuovi il nome del package. $AUTOLOAD =~ s/^Miodbm:://; # Passa il messaggio alla delega. $self->{'delega'}->$AUTOLOAD(@_); } package main; use Fcntl qw( O_RDWR O_CREAT ); tie %pippo, "Miodbm", "undbm", O_RDWR|O_CREAT, 0640; $pippo{'pluto'} = 123; print "pippo-pluto = $pippo{'pluto'}\n"; =head1 SI VEDA ANCHE L, L, L. =head1 TRADUZIONE =head2 Versione La versione su cui si basa questa traduzione E ottenibile con: perl -MPOD2::IT -e print_pod perlbot Per maggiori informazioni sul progetto di traduzione in italiano si veda http://pod2it.sourceforge.net/ . =head2 Traduttore Traduzione a cura di Raffaello Galli . =head2 Revisore Revisione a cura di dree.