2011-09-19 5 views
5

OK. Ho un problema nel tentativo di ereditare le costanti impostate in una classe genitore per una qualsiasi delle classi figlie.Costanti ereditabili con pacchetti in linea

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

package Car; 
use Exporter qw(import); 
our @EXPORT_OK = ('WHEELS', 'WINGS'); 

use constant WHEELS => 4; 
use constant WINGS => 0; 

sub new { 
    my ($class, %args) = @_; 
    my $self = { 
     doors => $args{doors}, 
     colour => $args{colour}, 
     wheels => WHEELS, 
     wings => WINGS, 
    }; 
    bless $self, $class; 
    return $self; 
} 

package Car::Sports; 
use base qw(Car); 

sub new { 
    my ($class, %args) = @_; 
    my $self = { 
     doors => $args{doors}, 
     engine => $args{engine}, 
     wheels => WHEELS, 
     wings => WINGS, 
    }; 
    bless $self, $class; 
    return $self; 
} 

package main; 
my $obj = Car->new(doors => 4, colour => "red"); 
print Dumper $obj; 

my $obj2 = Car::Sports->new(doors => 5, engine => "V8"); 

print Dumper $obj2; 
__END__ 

L'errore è:

Bareword "WHEELS" not allowed while "strict subs" in use at ./t.pl line 30. 
Bareword "WINGS" not allowed while "strict subs" in use at ./t.pl line 30. 
Execution of ./t.pl aborted due to compilation errors. 

Ora, io non sono venuto qui per spedire senza fare qualche ricerca. Comprendo che un'opzione potrebbe essere use Car qw(WHEELS WINGS) in Car::Sports. Tuttavia, se faccio che ottengo il seguente errore, perché le classi sono tutte in linea nello stesso file:

Can't locate Car.pm in @INC 

Per una serie di ragioni, ho bisogno di tenere i miei pacchetti in un unico file. C'è un modo per aggirare questo? Poiché le costanti sono fondamentalmente solo subs, perché devo importarle quando lo stesso non sarebbe vero per un metodo normale?

Infine, so anche che posso fare questo:

package Car::Sports; 
use base qw(Car); 

sub new { 
    my ($class, %args) = @_; 
    my $self = { 
     doors => $args{doors}, 
     engine => $args{engine}, 
     wheels => Car::WHEELS, 
     wings => Car::WINGS, 
    }; 
    bless $self, $class; 
    return $self; 
} 

E va bene ... Ma ho un numero di classi e vuole fare l'eredità delle costanti più generico che dover nominare il genitore classe esplicitamente (e a volte non è solo la classe genitore, ma il nonno).

Molte grazie in anticipo per eventuali suggerimenti!

Acclamazioni

risposta

6

Una soluzione è di includere la linea di

 
package Car::Sports; 
use base qw(Car); 
Car->import(qw(WHEELS WINGS)); 

E utilizzare i sigilli nella Car::Sports costruttore:

... 
wheels => &WHEELS, 
wings => &WINGS, 
... 

La classe Car non è definire la sua @EXPORTS_OK elencare fino all'esecuzione. I sigilli sono necessari perché il costruttore Car::Sports viene analizzato in fase di compilazione e il compilatore non sa che dovrebbero esserci i simboli WHEELS e WINGS nello spazio dei nomi Car::Sports.


L'unico modo per evitare i sigilli è quello di definire le esportazioni Car s' a tempo di compilazione:

package Car; 
our @EXPORT_OK; 
BEGIN {@EXPORT_OK = qw(WHEELS WINGS)} # set at compile not run time 
... 

package Car::Sports; 
use base qw(Car); 
BEGIN {Car->import('WHEELS','WINGS')} # import before c'tor is parsed 

Si potrebbe anche evitare queste macchinazioni definendo la classe Car nel proprio Car.pm file. Poi si sarebbe solo dire

use Car qw(WHEELS WINGS); 

e tutto nel file Car.pm sarebbe essere analizzato al momento della compilazione, e il metodo Exporter::import (innescato da una chiamata a Car::import) sarebbe automaticamente ottenere correre e importare i simboli desiderati per lo spazio dei nomi corrente .

+0

Perfetto! L'hai spiegato davvero bene e mi hai insegnato qualcosa! – wawawawa

+0

Inoltre, rendendo 'Car :: Sports' una sottoclasse di' Car' darà accesso a 'Car :: Sports' ai metodi di' Car' * *, ma non le sue * funzioni * come '& WHEELS' e' & WINGS'. Chiamare 'use base qw (Car)' è superfluo finché non si aggiungono altri metodi alla classe 'Car'. – mob

3

Questo cambiamento è adatto alle vostre esigenze?

[...] 
    wheels => $class->SUPER::WHEELS, 
    wings => $class->SUPER::WINGS, 
    [...] 

Utilizzando Data :: Dumper si ottiene:

$VAR1 = bless({ 
      'wings' => 0, 
      'colour' => 'red', 
      'doors' => 4, 
      'wheels' => 4 
      }, 'Car'); 
$VAR1 = bless({ 
      'wings' => 0, 
      'engine' => 'V8', 
      'doors' => 5, 
      'wheels' => 4 
      }, 'Car::Sports'); 
+0

Grande ... Grazie per questo. Stavo giocando con 'SUPER', ma non ho usato' $ class'. – wawawawa

3

alternativa, si potrebbe fare esattamente quello che fa use:

BEGIN { 
    package Car; 
    use Exporter qw(import); 
    @EXPORT_OK = qw(WHEELS); 

    ... 

    $INC{'Car.pm'} = 1; 
} 

BEGIN { 
    package Car::Sports; 

    use Car qw(WHEELS); 
    @ISA = 'Car'; 

    ... 

    $INC{'Car/Sports.pm'} = 1; 
} 
+0

ikegami - Penso di ricordarti di Perlmonks! Apprezzo i commenti, grazie! – wawawawa

0

In generale, esponendo che qualcosa è una costante a qualsiasi pacchetto diverso da quello che lo definisce è in realtà una cattiva idea. Ciò, tra le altre cose, discute dall'usare forme insolite quando ci si riferisce a valori che sono costanti in altre aree del codice.

Il modulo constant supporta in realtà una forma di invocazione che nasconde il fatto che stiamo parlando di costanti, in quanto chiamando costanti come metodi della classe funziona bene:

package Car; 
use constant default_wheel_count => 4; 

package Car::Sports; 

sub new { 
    my ($class) = @_; 

    return bless { 
     wheels => $class->default_wheel_count, 
    } => $class; 
} 

Ecco come si eredita effettivamente costanti, ma è ancora probabilmente l'approccio sbagliato. Eliminare il copypasta usando solo le costanti delle classi che implementano la costruzione di quegli attributi è la cosa giusta da fare.