NAME
perlbot - La scatola degli attrezzi degli Oggetti
DESCRIZIONE
La seguente raccolta di trucchi e suggerimenti è destinata a stimolare la curiosità a proposito di cose come l'utilizzo di variabili di istanza e i meccanismi delle relazioni tra oggetto e classe. Il lettore è incoraggiato a consultare dei testi pertinenti a proposito di definizioni e metodologie per la programmazione OO (orientata agli oggetti). Questo documento non è inteso come una guida per principianti per la programmazione orientata agli oggetti né 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 perlboot, perltoot e perltooc.
Il motto del Perl è ancora valido: ci sono molti modi per fare le cose.
SUGGERIMENTI DI SCALABILITÀ OO
Non tentate di verificare il tipo di $self. Non funzionerà se la classe è ereditata, quando il tipo di $self è valido ma il suo package non è quello che vi aspettereste. Si veda la regola numero 5.
Se viene utilizzata una sintassi orientata agli oggetti (OO) o indiretta per gli oggetti (IO), allora l'oggetto è probabilmente del tipo corretto e non ci sono motivi per essere paranoici al riguardo. In ogni caso, Perl non è 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.
Utilizzate la forma a due argomenti per il bless(). Permettete ad una sottoclasse di utilizzare il vostro costruttore. Si veda "EREDITARE UN COSTRUTTORE".
La sottoclasse è autorizzata a sapere cose che riguardano la sua immediata superclasse, la superclasse non è autorizzata a sapere alcunché di una sottoclasse.
Non abbiate il grilletto facile con l'ereditarietà. Relazioni di "utilizzo", "contenimento" o "delegazione" (qualche sorta di aggregazione, almeno) sono spesso più appropriate. Si veda "RELAZIONI TRA OGGETTI", "USARE RELAZIONI CON SDBM", e "DELEGAZIONE".
L'oggetto è il namespace. Rendete le variabili globali del package accessibili mediante l'oggetto. Ci<ograve> toglier<agrave> il bisogno di tirare a indovinare il package da cui proviene il simbolo. Si veda "IL CONTESTO DI CLASSE E L'OGGETTO".
La sintassi IO è sicuramente meno vistosa, ma anche incline ad ambiguità che possono causare errori difficili da trovare. Lasciate che le persone utilizzino la sintassi con la freccia, anche se non vi piace.
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 più. Come se non bastasse, state alimentando la paranoia di cui alla regola 2.
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 "PENSARE AL RIUTILIZZO DEL CODICE".
VARIABILI DI ISTANZA
Per mantenere le variabili di istanza è 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";
VARIABILI SCALARI DI ISTANZA
Quando serve solamente una variabile di istanza, può 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";
EREDITARIETÀ DI VARIABILI DI ISTANZA
Questo esempio mostra come si possano ereditare variabili di istanza da una superclasse, per inclusione nella nuova classe. Ciò 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";
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";
SOVRAPPORRE I METODI DELLA SUPERCLASSE
L'esempio seguente mostra come sovrapporre un metodo della superclasse e quindi chiamare il metodo sovrapposto. La pseudo-classe SUPER permette al programmatore di chiamare un metodo sovrapposto di una superclasse senza in realtà sapere dove quel metodo è 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 SUPER
si riferisce alla superclasse del package corrente (Pippo
), non alla superclasse di $self
.
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";
PENSARE AL RIUTILIZZO DEL CODICE
Una forza dei linguaggi Orientati agli Oggetti è la facilità con la quale il vecchio codice può utilizzare il nuovo codice. Il seguente esempio mostrerà 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 mostrerà che è 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 ciò non può accadere perché 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;
IL CONTESTO DI CLASSE E L'OGGETTO
Utilizzate l'oggetto per risolvere problemi riguardanti il contesto di package e classe. Tutto ciò di cui ha bisogno un metodo dovrebbe essere disponibile attraverso l'oggetto o dovrebbe essere passato come un parametro a quel metodo.
Una classe avrà 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 ciò accade, la superclasse può non sapere come trovare la nuova copia dei dati.
Questo problema può essere risolto utilizzando l'oggetto per definire il contesto del metodo. Permettete al metodo di cercare nell'oggetto un riferimento ai dati. L'alternativa è forzare il metodo per farlo andare a caccia dei dati ("È nella mia classe o in una sottoclasse? Quale sottoclasse?"), e ciò può non essere conveniente e porterà a scrivere codice strambo. È 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;
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 sarà un PLUTO e non un PIPPO, anche se il costruttore è 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;
DELEGAZIONE
Per alcune classi, tipo la SDBM_File, non si possono creare efficacemente sottoclassi perché 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 permetterà 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";
SI VEDA ANCHE
TRADUZIONE
Versione
La versione su cui si basa questa traduzione è ottenibile con:
perl -MPOD2::IT -e print_pod perlbot
Per maggiori informazioni sul progetto di traduzione in italiano si veda http://pod2it.sourceforge.net/ .
Traduttore
Traduzione a cura di Raffaello Galli <galliraf at googlemail punto com>.
Revisore
Revisione a cura di dree.