使用内联包继承常量

时间:2011-09-19 16:13:04

标签: perl

行。我在尝试继承任何子类的父类中设置的常量时遇到问题。

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

错误是:

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.

现在,我没有做过一些研究而没有来这里发帖。我理解,use Car qw( WHEELS WINGS)中的一个选项是Car::Sports。但是,如果我这样做,我会收到以下错误,因为这些类都在同一个文件中内联:

Can't locate Car.pm in @INC 

由于各种原因,我需要将我的包保存在一个文件中。有没有解决的办法?由于常量基本上只是潜艇,为什么我必须导入它们,而对于普通方法则不一样?

最后,我也知道我可以这样做:

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

这很好......但我有很多类,并希望使常量的继承更通用,必须明确地命名父类(有时它不仅仅是父类,而是祖父母)。 / p>

非常感谢任何提示!

干杯

4 个答案:

答案 0 :(得分:6)

一种解决方法是包含该行

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

AND 使用Car::Sports构造函数中的符号:

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

您的Car类在运行时之前未定义其@EXPORTS_OK列表。这些标记是必需的,因为Car::Sports构造函数在编译时被解析,编译器不知道WHEELS命名空间中应该有WINGSCar::Sports个符号。


避免这些印记的唯一方法是在编译时定义Car的导出:

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

您还可以通过在自己的Car文件中定义Car.pm类来避免这些阴谋。然后你会说

use Car qw(WHEELS WINGS);

并且Car.pm文件中的所有内容都将在编译时进行解析,并且Exporter::import方法(通过调用Car::import触发)会自动运行并导入所需的符号你当前的名字空间。

答案 1 :(得分:3)

愿这种变化适合您的需求吗?

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

使用Data :: Dumper:

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

答案 2 :(得分:3)

替代方案,你可以做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;
}

答案 3 :(得分:0)

通常情况下,除了定义它之外的任何包,暴露某些东西是常量实际上是一个坏主意。除其他事项外,这种观点反对在引用代码的其他区域中常见的值时使用不寻常的形式。

constant模块实际上支持一个隐藏我们正在讨论常量这一事实的调用表单,因为调用常量作为类方法可以正常工作:

package Car;
use constant default_wheel_count => 4;

package Car::Sports;

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

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

这就是人们实际上如何继承常量,但它仍然可能是错误的方法。通过仅使用实现构造这些属性的类中的常量来消除copypasta是正确的事情。