Breve introduzione a Moose
Perl, fin dal 1994 con il rilascio della versione 5, ha le caratteristiche proprie di un linguaggio orientato ad oggetti. La programmazione ad oggetti in Perl 5 è comunque in qualche modo del genere "fai da te", poiché secondo il classico stile Perl fornisce un supporto minimo incorporato nel linguaggio, e lascia che vi occupiate voi del resto.
Il maggior beneficio di questo stile "fai da te" sta nel fatto che il linguaggio impone molte poche restrizioni riguardo a come fare le cose. Per contro, ha lo svantaggio di essere in qualche modo intimidatorio verso i principianti. Ad ogni modo è diventato familiare.
Ulteriore beneficio è il fatto che queste caratteristiche sono state libere di evolversi e, dal momento che la programmazione ad oggetti non è imposta dal linguaggio, alcuni programmatori molto abili hanno avuto la facoltà di esplorare modi di fare le cose che altrimenti non avrebbero potuto essere concepiti. Moose è il prodotto di questa esplorazione e sta diventando rapidamente lo standard de facto per la programmazione ad oggetti in Perl. Oggi esploreremo i fondamenti della creazione e dell'uso di oggetti con Moose.
Nella programmazione ad oggetti Perl convenzionale, ci sono tre cose principali che avete bisogno di fare. Queste sono:
- Creazione di oggetti
- Attributi e Accessor
- Subclassing
Condurremo un veloce esame di questi aspetti, mettendo a confronto la tecnica di Perl e quella di Moose.
Creazione di oggetti
In Perl "puro", creare un oggetto consiste nel creare una variabile e
poi "blessare" un riferimento a tale variabile in una classe. Potete
farlo con qualunque tipo di variabile, ma più frequentemente si fa con
un riferimento ad hash. Tipicamente si fa in una subroutine chiamata
new
:
package FooClass; sub new { my $class = shift; my $foo = {}; bless $foo, 'FooClass'; return $foo; }
Questo vi mette a disposizione un oggetto, ma quasi nient'altro. Con Moose, lo si può fare in modo un po' più semplice:
package FooClass; use Moose;
E basta. Moose crea la routine new
per voi. Se la vostra classe ha
degli attributi, Moose si occuperà anche di gestirne la popolazione,
basandosi sugli argomenti eventualmente passati.
Creare un'istanza di una classe segue la stessa convenzione del Perl
"puro": semplicemente si chiama new
sulla classe. Moose, ad ogni
modo, vi permette di passare i valori iniziali per gli attributi nella
chiamata a new
. Per esempio:
FooClass->new( name => 'bob' );
Si tratta di un pratico pezzettino delle funzionalità "gratuite" che avrete con Moose.
Attributi e accessor
In Perl "puro", il vostro oggetto è (di frequente) un hash e i vostri attributi sono semplicemente membri di tale hash. Spesso, agli attributi si accede direttamente:
$foo->{name};
Questo è però disapprovato, in genere, poiché fornisce pochissima
struttura e rende molto facile dichiarare gli attributi erroneamente
senza volerlo. $foo->{naem}
può rivelarsi un bug molto difficile da
scovare. La "buona pratica" generalmente accettata consiste nel creare
subroutine di accesso per le vostre variabili: questo limita chi
utilizza il vostro oggetto a lavorare con gli attributi che avete
creato (gli esperti di programmazione ad oggetti si riferiscono a
tutto ciò dicendo "incapsulamento"). Il problema è che in Perl "puro"
dovete create cose di questo tipo da soli:
sub name { my $self = shift; if( @_ ) { $self->{'name'} = $_[0]; } return $self->{'name'}; }
È un sacco di codice, per avere solo get e set degli attributi di un oggetto. Per il Perl "puro", è nato un modulo CPAN per rendere le cose più facili. Class::Accessor rende la creazione di accessor più semplice:
package FooClass; use base qw(Class::Accessor); FooClass->mk_accessors(qw(name age));
Class::Accessor
crea il metodo new per voi. È chiaramente meglio che
farlo a mano. A questo punto è praticamente impossibile la creazione
accidentale di attributi, o l'accesso a quelli sbagliati. Non vi
impedisce in alcun modo di buttare spazzatura negli attributi. Questo
è perfettamente lecito:
$foo = FooClass->new(); $foo->name(192); $foo->age('cupcake');
Moose, invece, non solo fa quello che fa Class::Accessor, ma aggiunge controlli di tipo (ed altre amenità). Lo stesso oggetto creato con Moose:
package FooClass; use Moose; has 'name' => ( is => 'rw', isa => 'Str' ); has 'age' => ( is => 'rw', isa => 'Int' ); 1;
Il codice riportato sopra fornisce i medesimi metodi name
ed age
cui eravamo avvezzi. Ma se proviamo ad impostare 'cupcake'
come
age
otterremo un errore. Inoltre otteniamo l'abilità di dire, in
sostanza, "questo attributo non può essere cambiato una volta che
l'oggetto è stato creato". (Tra le altre cose, il sistema di attributi
e di controllo sui tipi di Moose è estremamente flessibile e merita un
articolo dedicato).
Subclassing
Nella programmazione ad oggetti il subclassing è progettato per permettere la specializzazione di una particolare classe. L'esempio classico è quello in cui avete una classe Shape che può fornire certe funzionalità, e quando vi serve una classe Square potete fare una sottoclasse aggiungendo solamente il codice che rende Square unica. In Perl "puro" fareste così:
package Square; @ISA = ("Shape"); # il resto della classe Square
Perl conosce le malizie per cercare i metodi nelle classi definite in
@ISA
, se non può trovarli nella classe Square. Funziona bene, ma
non è proprio intuitivo.
Anche Moose permette di creare sottoclassi, e rende particolarmente chiaro quel che state facendo:
package Square; use Moose; extends 'Shape'; # il resto della classe Square
Piuttosto lineare.
Altre cose da Moose
Moose non fa nulla che non possiate già fare in in Perl "puro". È costruito sulle stesse caratteristiche proprie della programmazione ad oggetti che sono supportate da Perl. Si occupa, però, di un'enorme quantità di lavoro che dovreste altrimenti fare da soli, lasciandovi liberi di lavorare sulle nuove funzionalità anziché reinventare la ruota.
In questo articolo vi ho mostrato come fare le cose "normali" con Moose. A questo punto sapete usare la programmazione ad oggetti di base, e potreste cominciare a sostituire le vostre classi costruite a mano con quelle costruite con Moose.
Moose, tutto sommato, è più di un po' di sintassi furba e di stratagemmi salva-tempo. Moose possiede caratteristiche aggiuntive che rendono un piacere lavorarci. Le elencherò solamente, visto che richiederebbero un libro per essere trattate propriamente. Alcune delle caratteristiche disponibili in Moose sono:
- Metadati di classi: la possibilità di far sì che il vostro codice esamini la struttura dei vostri oggetti
- "Coercion" - l'abilità di convertire valori da un tipo all'altro quando è necessario
- Modificatori di metodo - la possibilità di aggiungere codice che venga lanciato prima, durante o "attorno" un metodo esistente
- Ruoli - l'abilità di aggiungere funzionalità pre-definite alle classi, senza crearne sottoclassi
Ce ne sono molte altre e vale la pena di esplorarle a loro volta. I ruoli sono particolarmente interessanti e forniscono una flessibilità inaudita in quasi ogni linguaggio di programmazione. Vale inoltre la pena di notare che numerose estensioni di Moose forniscono ulteriori funzionalità, al di là di Moose standard.
Per imparare altro su Moose, fate riferimento al manuale. Per altri dettagli su Moose e il rapporto con Perl "puro", controllare la versione non ancora "sgrezzata" del manuale.
Potete anche visitare la homepage di Moose. Alla fine, ho cominciato una lista di "spigolature Moose" sul Wiki Catalyzed, per tenere traccia delle parti più insidiose di Moose in cui mi sono imbattuto. Ritenetevi liberi di aggiungere le vostre, man mano che scoprirete le gioie di Moose.