2010-08-11 11 views
10

Diciamo che avere un genitore di classe Perl in un unico file:Le sottoclassi di Perl ereditano moduli e direttive importati?

#!/usr/bin/perl 
package Foo; 
use strict; 
use warnings; 

use Data::Dumper; 

sub new{ 
    my $class = shift; 
    my %self =(); 
    return bless %self, $class; 
} 
1; 

e una sottoclasse in un file diverso:

#!/usr/bin/perl 
package Bar; 
use base "Foo"; 
1; 

Sarà la sottoclasse ereditare le dichiarazioni di uso del genitore? So che il metodo nuovo verrà ereditato.

Fondamentalmente sto provando a ridurre la quantità di boilerplate nel mio codice e non riesco a trovare una risposta chiara a questa domanda.

+0

Chiedo informazioni su questa funzionalità perché Test :: Most e Moose dichiarano di farlo, ma non ho capito come lo fanno. – chotchki

+3

Esaminare il metodo import() in Test :: Most. Ecco come funziona Carica manualmente tutti i moduli boilerplate e li esporta di due livelli. –

+0

Grazie a tutti per l'aiuto di tutti! Il sub di importazione era la chiave. (Potrei solo salvarmi il dolore futuro e andare ad Alci). – chotchki

risposta

4

Ah, bella domanda!

Will the subclass inherit the use statements from the parent? 

Beh, questo dipende da cosa intendi per ereditarietà. Non farò nessuna supposizione fino alla fine, ma la risposta è forse. Vedete, perl mescola le idee di Classes e Namespaces - un package è un termine che può descrivere entrambi. Ora il problema è la dichiarazione use tutto ciò che fa è forzare l'inclusione di un pacchetto e chiamare i target import() sub. Ciò significa che ha essenzialmente un controllo illimitato sul tuo pacchetto e in base alla tua classe.

Ora, questo composto con tutti i metodi in perl è nient'altro che subs che prende $self come primo argomento per convenzione e si rimane con perl5. Questo ha un enorme vantaggio per coloro che sanno come usarlo. Mentre strict è un pragma lessicale, che dire di Moose?

package BigMooseUser; 
use Moose; 

package BabyMooseUser; 
our @ISA = 'BigMooseUser'; 

package Foo; 
my $b = BabyMooseUser->new; 
print $b->meta->name; 

Ora, dove ha fatto BabyMooseUser ottenere il costruttore (nuovo) da? Da dove ha preso la meta class? Tutto questo è fornito da un singolo use Moose; nella classe genitore (spazio dei nomi). Così

Will the subclass inherit the use statements from the parent? 

Bene, qui, nel nostro esempio, se gli effetti della dichiarazione uso sono da aggiungere metodi, che di certo.

Questo argomento è piuttosto profondo e dipende se si sta parlando di pragma o di quadri di oggetti più oscuri o moduli procedurali. Se vuoi mitigare lo spazio dei nomi di un genitore di influire sul tuo proprio nel paradigma OO vedi namespace::autoclean.

+1

In realtà, l'uso non impone l'inclusione di un pacchetto.Cerca un file basato sul nome del pacchetto che si desidera utilizzare(). Quel file non deve caricare quel pacchetto. Per convenzione, chiamiamo i nostri file con il nome del pacchetto che contiene, ma non è qualcosa a cui si usa use(). Ad esempio, Module :: Build :: Version è davvero lì per caricare la versione. I carichi use() del file potrebbero anche avere più di un pacchetto. –

+2

BabyMooseUser ottiene il suo nuovo() perché si dichiara una relazione di ereditarietà. La singola chiamata di Moose non lo imposta per te. E l'autoclean si occuperà solo dei simboli importati. Non limiterà l'accesso a qualsiasi altra cosa tu voglia ottenere. –

+2

È strano dire che i metodi sono sottotitoli che * prendono * $ self come primo argomento. I metodi sono subs, ma si finisce con il loro referente come primo argomento senza specificarlo esplicitamente come argomento. Quel referente potrebbe essere il nome della classe (una stringa) o un riferimento a un oggetto, a seconda di ciò che è a sinistra della -> (ignorando le chiamate indirette). Inoltre, non è semplice quanto cercare letterali o variabili sul lato sinistro. :) –

1

È possibile ottenere una risposta definitiva esaminando le tabelle di simboli per ogni pacchetto:

# examine-symbol-tables.pl 
use Bar; 

%parent_names = map{$_ => 1} keys %Foo::; 
%child_names = map{$_ => 1} keys %Bar::; 

delete $parent_names{$_} && ($common_names{$_} = delete $child_names{$_}) foreach keys %child_names; 

print "Common names in symbol tables:\n"; 
print "@{[keys %common_names]}\n\n"; 

print "Unique names in Bar symbol table:\n"; 
print "@{[keys %child_names]}\n\n"; 

print "Unique names in Foo symbol table:\n"; 
print "@{[keys %parent_names]}\n\n"; 

 
$ perl inherit.pl 
Common names in symbol tables: 
BEGIN 

Unique names in Bar symbol table: 
ISA isa import 

Unique names in Foo symbol table: 
Dumper new VERSION 
4

Per la riduzione dello standard, ho un paio di strategie: la maggior parte delle mie classi sono classi Moose, che si occupa della configurazione di OO e inoltre mi fornisce severi e avvisi. Se voglio avere le funzioni disponibili in molti pacchetti, creerò un modulo MyProject::Util specifico del progetto che utilizza Sub-Exporter per fornirmi le mie funzioni e la mia interfaccia. Ciò lo rende più coerente e, se decido di cambiare il Dumper (ad esempio) in un secondo momento per qualsiasi ragione, non devo cambiare molto codice. Ciò ti consentirà anche di raggruppare le esportazioni.Una classe poi di solito simile a questa:

package Foo; 
use Moose; 
use MyProject::Util qw(:parsing :logging); 

use namespace::autoclean; 

# class implementation goes here 

1; 

Se ci sono altre cose che si considerano come boilerplate e desidera rendere più semplice da comprendere, ma ovviamente dipende da ciò che quelle cose sono.

7

Hai chiesto in un commento su Test::Most e in che modo riduce il boilerplate. Guarda il suo metodo import. Sta caricando i moduli nel suo spazio dei nomi, aggiungendo tali simboli a @EXPORT, quindi richiamando un altro import attraverso un goto per ottenerli infine nello spazio dei nomi di chiamata. È una seria magia nera che Curtis sta succedendo lì, anche se mi chiedo perché non abbia usato qualcosa come import_to_level. Forse ci sono alcuni effetti collaterali a cui non sto pensando.


parlo un po 'su questo genere di cose in Avoid accidently creating methods from module exports in The Effective Perler. È in un contesto diverso ma sono alcuni degli stessi problemi.

Ecco un esempio diverso.

Se un altro modulo carica un modulo, è possibile accedervi. Non è bello dipendere da questo però. Qui ci sono tre file separati:

Top.pm

use 5.010; 

package Top; 
use File::Spec; 

sub announce { say "Hello from top!" } 
1; 

Bottom.pm

package Bottom; 
use parent qw(Top); 

sub catfiles { File::Spec->catfile(@_) } 

1; 

test.pl

use 5.010; 

use Bottom; 

say Bottom->catfiles(qw(foo bar baz)); 

say File::Spec->catfile(qw(one two three)); 

Carico solo file :: Spec in Top.pm. Tuttavia, una volta caricato, posso utilizzarlo ovunque nel mio programma Perl. L'output mostra che ho potuto "usare" il modulo in altri file anche se ho caricato solo in uno:

Bottom/foo/bar/baz 
one/two/three 

A questo scopo, la parte del codice che carica il modulo deve caricare prima qualsiasi altra parte del codice tenta di utilizzare quel modulo. Come ho detto, è una cattiva idea dipendere da questo: le cose si rompono se la sequenza di caricamento cambia o il modulo di caricamento scompare.

Se si desidera importare simboli, tuttavia, è necessario caricare in modo esplicito il modulo desiderato mentre si è nel pacchetto in cui si desidera eseguire l'importazione. Questo è solo il modo in cui il modulo di esportazione definisce i simboli in quel pacchetto. Non è qualcosa che dipende dalla portata.

2

Una risposta pragmatica al problema: utilizzare o osservare come lo fa Modern::Perl per applicare severi e avvisi.

+0

Modern :: Perl richiama severi e warnings, che sono moduli che futz con $^H. La maggior parte dei moduli non vorranno farlo. –

Problemi correlati