在以前关于Class :: Struct vs Object :: Accessor的查询的基础上,我想找到从对象构造函数中的coderefs分配包subs的最佳方法。嘿,我有OO属性,所以让我们来看看方法:)
请注意,我说'最好'的方式。我已经找到了两种方法,它们似乎也是类似问题中的流行答案,但它们分别需要规避严格和警告。
请注意,该示例使类用户能够在构造时覆盖格式化方法。可怜的例子,但你明白了。为了先发制人地反驳OO纯粹主义者,这在我的特定设计中更有意义,而不是让类用户使用自己的重写版本对其进行子类化。
以下是我的两次尝试:
use 5.014;
use autodie;
use strict;
use warnings;
package Account {
use base 'Object::Accessor';
sub new {
my ($type, %args) = @_;
my $self = bless { }, $type;
$self->mk_accessors(qw(
first_name last_name age_in_years activated
));
$self->first_name( $args{first_name } // 'Default First Name' );
$self->last_name( $args{last_name } // 'Default Last Name' );
$self->age_in_years($args{age_in_years} // 'Default Age in Years');
$self->activated( $args{activated } // 'Default Activated' );
{
# Stop skim reading and look here!
no warnings 'once';
*formatted = $args{formatted} || sub {
return 'Default formatting routine';
};
}
return $self;
}
}
my $account = Account->new;
say $account->formatted;
# Output: Default formatting routine
另一个:
use 5.014;
use autodie;
use strict;
use warnings;
package Account {
use base 'Object::Accessor';
sub new {
my ($type, %args) = @_;
my $self = bless { }, $type;
$self->mk_accessors(qw(
first_name last_name age_in_years activated
));
$self->first_name( $args{first_name } // 'Default First Name' );
$self->last_name( $args{last_name } // 'Default Last Name' );
$self->age_in_years($args{age_in_years} // 'Default Age in Years');
$self->activated( $args{activated } // 'Default Activated' );
{
# Stop skim reading and look here!
no strict 'refs';
*{'formatted'} = $args{formatted} || sub {
return 'Default formatting routine';
};
}
return $self;
}
}
my $account = Account->new;
say $account->formatted;
# Output: Default formatting routine
前一种方法应该在没有警告的情况下工作,但它并不是因为Perl认为我没有在任何地方使用它并在运行时将其标记为警告。后者在没有规避严格的情况下是行不通的,那应该是怎样的:)
我正在做的就是为用户提供的coderef分配一个方法。应该有一种简单的方法。
请注意,定义调用用户定义的coderef的新子节点也不起作用,因为无法从中访问词法%args。
虽然我们分配包变量,但它不能做sub。
避免从严格和警告的花园中逃脱,这是最干净的方法是什么?地狱,有可能使用我不知道的Object :: Accessor模块来做到这一点。
答案 0 :(得分:2)
如果你想'格式化'是一个可自定义的每个对象的方法,并且没有覆盖每个构造函数调用的整个包/类(即所有对象)的方法(分配给包子会做),你可以将subref分配给对象“私有”密钥,并从普通方法调用它:
sub new {
my ($type, %args) = @_;
...
$self->{_formatted} = $args{formatted} // sub { 'default' };
}
sub formatted {
my ($self) = shift;
return $self->{_formatted}->($self,@_);
}
答案 1 :(得分:1)
它可以存储在$ self:
中$$self{formatted} = sub { ... };
或者放入词汇:
my $formatted = sub { 1; }; # Dummy sub for initial assignment
sub new
{
...
$formatted = sub { ... };
}
sub formatted { $formatted->(@_) }
或者只是忍受没有严格的'参考'。只要它像你上面那样在一个小的,包含良好的块中,我从未认为它是邪恶的。或者更确切地说,它是邪恶的,但却是一个小的,包含良好的。
答案 2 :(得分:1)
strict
制作,可以针对您想要执行您不想意外执行的操作的特定代码将其关闭。特别是,如果你这样做,就像你在做的那样,在尽可能小的范围内。它的工作方式与pragma在C中的工作方式相同。它允许您“破坏规则”,而不会在没有“规则”的情况下容易受到无数问题的影响。
但是,如果您不想关闭任何“规则”,那么您应该查看Symbol
。具体来说,qualify_to_ref
将返回对符号的引用,以便在将其转换回符号(见下文)时不会触发警报。
因此,对于您提供的代码,您可以这样做:
*{ Symbol::qualify_to_ref( 'formatted' ) }
= $args{formatted}
|| sub { return 'Default formatting routine'; }
;