使用 Autoload 本身就很困难。如果您想要一个为您制作访问器的实体对象系统,那么请使用 Moose、Mouse、Moo,或者只是循环您的字段并自己安装访问器:
BEGIN {
my @fields = qw/foo bar baz/;
for my $field (@fields) {
no strict 'refs';
# install a closure in the package stash.
*{ __PACKAGE__ . "::" . $field } = sub {
my $self = shift;
$self->{$field} = shift if @_;
return $self->{$field};
};
}
}
如果一个类可以AUTOLOAD
遇到未定义的方法,则AUTOLOAD
使用缺少的 sub 的参数调用 sub。所请求子的完全限定名称在$AUTOLOAD
包变量中传递。
一个典型的 Autoload sub 看起来像:
use Carp;
my %fields_allowed = map {$_ => 1} qw/foo bar baz/;
sub AUTOLOAD {
my $field = our $AUTOLOAD;
$field =~ s/.*:://; # strip the package name
$fields_allowed{$field}
or croak qq(Can't locate object method $field via package "@{[__PACKAGE__]}");
my $self = shift;
$self->{$field} = shift if @_;
return $self->{$field};
}
仍然存在两个问题:
- 当对象的引用计数降至零或线程终止时,
DESTROY
如果对象提供了 1,则在对象上调用该方法。DESTROY
我们可以通过提供一个空的实现来防止自动加载: sub DESTROY {}
.
- 我们可以询问任何对象是否可以执行某种方法,例如
say "Good dog" if $dog->can("roll")
. 因此,我们必须重写can
以支持我们的自动加载。该can
方法对于安全的鸭子类型很有用。每个对象都继承自,它为和UNIVERSAL
提供默认实现。can
isa
的约定can
是它采用方法的名称。undef
当对象无法执行该方法时,它将返回,或者如果可以,则返回对该方法的代码引用。一个合适的实现是
sub can {
my ($self, $name) = @_;
# check if it's a field of ours
if ($fields_allowed{$name}) {
return sub {
my $self = shift;
$self->{$name} = shift if @_;
return $self->{$name};
};
}
# Ask SUPER implementation of can if we can do $name
if (my $meth = $self->SUPER::can($name)) {
return $meth;
}
return; # no method found
}
我们现在可以简化AUTOLOAD
为
sub AUTOLOAD {
my $field = our $AUTOLOAD;
$field =~ s/.*:://; # strip the package name
my $code = $self->can($field)
or croak qq(Can't locate object method $field via package "@{[__PACKAGE__]}");
goto &$code; # tail call; invisible via `caller()`.
}
要做到这一点非常复杂。结论:不要使用 Autoload,因为您认为它可能会减少工作量。从来都不是。它对于实现代理模式非常有用,但这有点高级。
我敦促您在深入了解 Perl 独特而奇怪的特性之前,先了解一下 OO 基础知识和 Moose 对象系统。