使用typeglobs继承函数

时间:2013-08-31 08:26:11

标签: perl function inheritance perl-module typeglob

与第二个例子相比,第一个带有typeglobs的例子有缺点吗?

package Some::Module::Win32;
use strict;
use 5.10.0;

use Exporter 'import';
our @EXPORT_OK = qw(public);

use Some::Module;

*_func_one   = \&Some::Module::_func_one;
*_func_two   = \&Some::Module::_func_two;
*_func_three = \&Some::Module::_func_three;

sub public {
    my $s = _func_one();
    for my $i ( 0 .. $s ) {
        say _func_two( $i );
    }
    say _func_three( $s );
}

1;

package Some::Module::Win32;
use strict;
use 5.10.0;

use Exporter 'import';
our @EXPORT_OK = qw(public);

use Some::Module;

sub public {
    my $s = Some::Module::_func_one();
    for my $i ( 0 .. $s ) {
        say Some::Module::_func_two( $i );
    }
    say Some::Module::_func_three( $s );
}

1;

1 个答案:

答案 0 :(得分:3)

您的第一个示例种类显示了Exporter的工作原理:通过分配typeglobs。但是有一个重要的区别:导入函数时。当子程序有原型时,这一点非常重要。在解析期间需要知道原型,因此必须在BEGIN阶段知道。 use - 通常在已使用的包上调用import - 在BEGIN阶段处理。

您还应该意识到,在您的第一个示例中,代码的用户现在可以Some::Module::Win32::_func_one(),而在第二个示例中这是不可能的(除非Some::Module导出_func_one

这是一个在正确阶段导入函数的版本:

package Some::Module::Win32;
use strict;
use 5.10.1;  # because `parent` comes with 10.1

use parent 'Exporter';  # optimal way to inherit the `import`.
our @EXPORT_OK = qw(public);

use Some::Module;

BEGIN {
  *_func_one   = \&Some::Module::_func_one;
  *_func_two   = \&Some::Module::_func_two;
  *_func_three = \&Some::Module::_func_three;
}

sub public {
    my $s = _func_one();
    for my $i ( 0 .. $s ) {
        say _func_two( $i );
    }
    say _func_three( $s );
}

1;

如果您愿意,可以使用namespace::autoclean之类的模块,在您不再需要符号表时删除任何外来导入。

另一种策略是将coderefs放入词汇变量。但是,语法有点难看,任何原型完全被忽略:

package Some::Module::Win32;
use strict;
use 5.10.1;

use parent 'Exporter';
our @EXPORT_OK = qw(public);

use Some::Module;

my $func_one   = \&Some::Module::_func_one;
my $func_two   = \&Some::Module::_func_two;
my $func_three = \&Some::Module::_func_three;

sub public {
    my $s = $func_one->();
    for my $i ( 0 .. $s ) {
        say $func_two->( $i );
    }
    say $func_three->( $s );
}

1;

这不会触及符号表,因此可以被认为是非常“干净”的解决方案。

您的第二个版本可能更冗长,但它具有非常明确的优势,这可能使代码更容易理解为读者或维护者。这是我经常使用的解决方案。

  • 原型正常工作
  • 非常易读(除非函数的全名太长,例如Marpa::R2::Inner::Scanless::G::SYMBOL_IDS_BY_EVENT_NAME_AND_TYPE
  • 明确的
  • 没有命名空间污染