【发布时间】:2012-03-24 23:38:07
【问题描述】:
前注: 为了这个讨论,请让我们暂时忽略这样一个事实,即通过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);
在上面的例子中,如果我没记错的话,类是在运行时组合的,它的访问器是在类被实例化的同时创建的。这意味着创建对象时会有速度损失。
现在考虑以下替代方案:
#!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);
在这个版本中,组合是在BEGIN 块内进行的(即在编译时),我相信通过在程序的生命周期中尽快处理这个任务,我可以在运行时对象实例化期间节省时间。
一个简单的Benchmark,
# benchmark it!
package main;
use Benchmark qw/cmpthese/;
cmpthese(-2, {
accessors_new => sub { Person1->new },
accessors_begin => sub { Person2->new },
});
这些结果似乎支持了我的理论:
Rate accessors_begin accessors_new
accessors_begin 853234/s -- -9%
accessors_new 937924/s 10% --
假设到目前为止我的推理是正确的,
- 比较这两种策略还存在哪些其他优点/缺点?
- 依赖
BEGIN块作为执行此类类操作的有效方法是个好主意吗? - 什么时候不推荐?
【问题讨论】:
标签: perl oop module closures accessor