2011-09-19 8 views
5

OK. Tengo un problema al tratar de heredar las constantes establecidas en una clase principal para cualquiera de las clases secundarias.Constantes Heredadas con paquetes en línea

#!/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__ 

El error es:

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. 

Ahora, no he venido aquí para publicar sin hacer algunas investigaciones. Entiendo que una opción sería use Car qw(WHEELS WINGS) en Car::Sports. Sin embargo, si hago lo que me sale el siguiente error, porque las clases son en línea en el mismo archivo:

Can't locate Car.pm in @INC 

Por una variedad de razones, tengo que mantener mis paquetes en un solo archivo. ¿Hay alguna forma de evitar esto? Como las constantes son básicamente solo subs, ¿por qué tengo que importarlas cuando lo mismo no sería cierto para un método normal?

Por último, también sé que puedo hacer esto:

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; 
} 

Y está bien ... pero tengo un número de clases y quiere hacer la herencia de las constantes más genérico que tener que nombrar al padre clase explícitamente (y a veces no es solo la clase principal, sino el abuelo).

¡Muchas gracias de antemano por cualquier apuntador!

Saludos

Respuesta

6

Hay una solución que incluya la línea

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

Y utilizar los sellos en el Car::Sports constructor:

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

Su clase Car no va a definir su @EXPORTS_OK lista hasta tiempo de ejecución. Los sigilos son necesarios porque el constructor Car::Sports se analiza en tiempo de compilación y el compilador no sabe que deben existir los símbolos WHEELS y WINGS en el espacio de nombres Car::Sports.


La única manera de evitar los sellos es definir las exportaciones Car 's en tiempo de compilación:

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 

También podría evitar estas maquinaciones mediante la definición de la clase Car en su propia Car.pm archivo. Entonces usted acaba de decir

use Car qw(WHEELS WINGS); 

y todo en el archivo Car.pm se pueda analizar en tiempo de compilación, y el Exporter::import método (provocada por una llamada a Car::import) se consiguen automáticamente ejecutar e importar los símbolos deseados a su espacio de nombres actual .

+0

¡Perfecto! ¡Realmente lo has explicado bien y me enseñaste algo! – wawawawa

+0

Además, al hacer 'Car :: Sports' una subclase de' Car' le dará acceso a 'Car :: Sports' a' cars * methods *, pero no a sus * funciones * como '& WHEELS' y' & WINGS'. Llamar 'use base qw (Car)' es superfluo hasta que agregue algunos más métodos a la clase 'Car'. – mob

3

¿Este cambio se adapta a sus necesidades?

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

Uso de Datos :: Dumper que se obtiene:

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

Genial ... Gracias por esto. Estaba jugando con 'SUPER', pero no usé' $ class'. – wawawawa

3

alternativa, se podría hacer exactamente lo que hace 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 - ¡Creo que te recuerdo de Perlmonks! Aprecio los comentarios, gracias! – wawawawa

0

En general, exponiendo que algo es una constante a cualquier paquete aparte de la que lo define es en realidad una mala idea. Esto argumenta, entre otras cosas, contra el uso de formas inusuales cuando se refiere a valores que son constantes en otras áreas de su código.

El módulo constant realmente apoya una forma de invocación que oculta el hecho de que estamos hablando de constantes, ya que llamar constantes como los métodos de clase funciona bien:

package Car; 
use constant default_wheel_count => 4; 

package Car::Sports; 

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

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

Así es como uno realmente hereda constantes, pero aún es probablemente el enfoque equivocado. Eliminar el copypasta usando solo las constantes de las clases que implementan la construcción de esos atributos es lo correcto.