Sér*_*ino 7 oop perl closures module accessor
前言注意: 为了便于讨论,请暂时忽略这样一个事实:使用Class :: Accessor可以实现相同的目的,甚至只需使用Moose(在考虑代码可读性时可能会有更好的结果)和可维护性).
关于面向对象的Perl,本书Programming Perl讨论了使用闭包生成存取方法的能力.例如,这是一段有效的代码:
#!perl
use v5.12;
use warnings;
# at run-time
package Person1;
my @attributes = qw/name age address/;
for my $att ( @attributes )
{
my $accessor = __PACKAGE__ . "::$att";
no strict 'refs'; # allow symbolic refs to typeglob
*$accessor = sub {
my $self = shift;
$self->{$att} = shift if @_;
return $self->{$att};
};
}
sub new { bless {}, shift }
package main;
use Data::Dumper;
my $dude = Person1->new;
$dude->name('Lebowski');
say Dumper($dude);
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,如果我没有弄错的话,该类是在运行时组成的,其访问器是在实例化类的同时创建的.这意味着对象创建将受到速度惩罚.
现在考虑以下替代方案:
#!perl
use v5.12;
use warnings;
package Person2;
BEGIN
{
for my $att (qw/name age address/)
{
my $accessor = __PACKAGE__ . "::$att";
no strict 'refs'; # allow symbolic refs to typeglob
*$accessor = sub {
my $self = shift;
$self->{$att} = shift if @_;
return $self->{$att};
};
}
}
sub new { bless {}, shift }
package main;
use Data::Dumper;
my $dude = Person2->new;
$dude->name('Lebowski');
say Dumper($dude);
Run Code Online (Sandbox Code Playgroud)
在这个版本中,组合是在一个BEGIN块内(即在编译时),我相信通过在程序的生命周期中尽快处理这个任务,我在对象实例化期间节省了时间运行.
一个简单的Benchmark,
# benchmark it!
package main;
use Benchmark qw/cmpthese/;
cmpthese(-2, {
accessors_new => sub { Person1->new },
accessors_begin => sub { Person2->new },
});
Run Code Online (Sandbox Code Playgroud)
这些结果似乎支持我的理论:
Rate accessors_begin accessors_new
accessors_begin 853234/s -- -9%
accessors_new 937924/s 10% --
Run Code Online (Sandbox Code Playgroud)
假设到目前为止我的推理是正确的,
BEGIN块作为进行这种类操作的有效方法是一个好主意吗?Sch*_*ern 10
当我运行你的基准测试时,我会受到很大的影响,这可能会导致你的差异.对于10%或更小的差异,运行几次以确定.
Rate accessors_begin accessors_new
accessors_begin 1865476/s -- -4%
accessors_new 1943339/s 4% --
Rate accessors_begin accessors_new
accessors_begin 1978799/s -- -1%
accessors_new 2001062/s 1% --
Rate accessors_new accessors_begin
accessors_new 1943339/s -- -2%
accessors_begin 1988089/s 2% --
Rate accessors_begin accessors_new
accessors_begin 1796509/s -- -8%
accessors_new 1949296/s 9% --
Rate accessors_begin accessors_new
accessors_begin 1916122/s -- -3%
accessors_new 1969595/s 3% --
Run Code Online (Sandbox Code Playgroud)
但实际上你所有的基准测试都是sub new { bless {}, shift }.对自己进行基准测试同样会强调颤动.生成访问器的工作已经在代码加载时完成,并且从未进入,BEGIN阻止与否.
Perl没有单个编译时和运行时.相反,每个used,required或evaled都经历了它自己的编译和运行时步骤. use Some::Class导致Some/Class.pm编译和运行时执行BEGIN,编译子程序,然后执行任何其他代码.代码是在模块内的BEGIN块内部还是外部对于该模块外部的代码没有什么区别.