我有一组字段,每个字段都有不同的验证规则集。
我已经放置了子例程引用来验证hash-ref。
目前它在我的构造函数中,但我想从私有子中的构造函数中取出它。
我已经完成了如下
sub new {
my $class = shift;
my $self = {@_};
$class = (ref($class)) ? ref $class : $class;
bless($self, $class);
$self->{Validations} = {
Field1 => {name => sub{$self->checkField1(@_);},args => [qw(a b c)]}
Field2 => {name => sub{$self->checkField2(@_);},args => {key1, val1}}
..
..
..
..
};
return $self;
}
现在我想从我的构造函数中取出所有这些验证规则,并希望做下面的事情,这样我就可以更好地控制基于类型字段的验证规则。(说一些规则在一个中很常见)我可以通过覆盖字段的值来覆盖其他规则的规则。)
bless($self, $class);
$self->{Validations} = $self->_getValidation($self->{type});
return $self;
}
sub _getValidation{
my ($self,$type) = @_;
my $validation = {
Field1 => {name => sub {$self->checkField1(@_);}, args => {key1 => val1}},};
return $validation;
}
但我得到Can't use string ("") as a subroutine ref while "strict refs" in use at...
任何人都可以告诉我为什么这个行为与sub ref。如果我检查我的名字密钥,它将变为null或sub {DUMMY};
答案 0 :(得分:5)
在我看来,你正在接近重塑Moose。考虑使用Moose而不是构建类似的东西,但不太有用。
错误消息表示您在代码需要代码引用的位置传入字符串。获取堆栈跟踪以确定错误的来源。
您可以使用Carp :: Always,覆盖$SIG{__DIE__}
处理程序来生成堆栈跟踪,或在代码中插入Carp::confess
。
这是一个sigdie解决方案,将其粘贴在您的代码中,它将在模块初始化之前运行:
$SIG{__DIE__} = sub { Carp::confess(@_) };
您可能需要将其放在BEGIN
块中。
我真的很想阻止你采用这种方法来构建对象。作为对象的一部分,您乐意将任何传递给构造函数的随机垃圾祝福!你轻快地伸手进入你的物体内部。字段验证规则* 不属于构造函数 - 它们属于属性mutators。
如果您必须使用DIY对象,请清理您的做法:
# Here's a bunch of validators.
# I set them up so that each attribute supports:
# Multiple validators per attribute
# Distinct error message per attribute
my %VALIDATORS = (
some_attribute => [
[ sub { 'foo1' }, 'Foo 1 is bad thing' ],
[ sub { 'foo2' }, 'Foo 2 is bad thing' ],
[ sub { 'foo3' }, 'Foo 3 is bad thing' ],
],
other_attribute => [ [ sub { 'bar' }, 'Bar is bad thing' ] ],
);
sub new {
my $class = shift; # Get the invocant
my %args = @_; # Get named arguments
# Do NOT make this a clone method as well
my $self = {};
bless $class, $self;
# Initialize the object;
for my $arg ( keys %args ) {
# Make sure we have a sane error message on a bad argument.
croak "Bogus argument $arg not allowed in $class\n"
unless $class->can( $arg );
$self->$arg( $args{$arg} );
}
return $self;
}
# Here's an example getter/setter method combined in one.
# You may prefer to separate get and set behavior.
sub some_attribute {
my $self = shift;
if( @_ ){
my $val = shift;
# Do any validation for the field
$_->[0]->($val) or croak $_->[1]
for @{ $VALIDATORS{some_attribute} || [] };
$self->{some_attribute} = $val;
}
return $self->{some_attribute};
}
所有这些代码都非常好,但您必须为每个属性重复属性代码。这意味着很多容易出错的样板代码。您可以通过学习使用闭包或字符串eval来动态创建方法来解决这个问题,或者您可以使用Perl的许多类生成库之一,如Class :: Accessor,Class :: Struct,Accessor :: Tiny等等。
或者你可以学习[驼鹿] [3]。 Moose是新的(ish)对象库,它已经接管了Perl OOP实践。它提供了一组强大的功能,与传统的Perl OOP相比大大减少了样板:
use Moose;
type 'Foo'
=> as 'Int'
=> where {
$_ > 23 and $_ < 42
}
=> message 'Monkeys flew out my butt';
has 'some_attribute' => (
is => 'rw',
isa => 'Foo',
);
答案 1 :(得分:2)
我没有读过你所拥有的一切,但这让我感到震惊:
sub new {
my $class = shift;
my $self = {@_};
$class = (ref($class)) ? ref $class : $class;
bless($self, $class);
通常,当您创建新对象时,用户不会将$self
作为其中一个对象传递。这就是你正在创造的东西。
您通常会看到以下内容:
sub new {
my $class = shift; #Contains the class
my %params = @_; #What other parameters used
my $self = {}; #You're creating the $self object as a reference to something
foreach my $param (keys (%params)) {
$self->{$param} = $params{$param};
}
bless ($self, $class) #Class is provided. You don't have to check for it.
return $self #This is the object you created.
}
现在,$self
不必像上例中那样是对哈希的引用。它可以是对数组的引用。或者也许是一个功能。但是,它通常是一个参考。重点是,用户没有传递$self
,因为它是由new
子例程创建的。
在调用$class
子例程时,您是否必须检查new
的值。
如果您想在私人课程中进行验证(顺便提一下,这是一个很好的主意),您可以在bless
之后执行此操作:
sub new {
my $class = shift; #Contains the class
my %params = @_; #What other parameters used
my $self = {}; #You're creating the $self object as a reference to something
foreach my $param (keys (%params)) {
$self->{$param} = $params{$param};
}
bless ($self, $class) #Class is provided. You don't have to check for it.
#Now you can run your verifications since you've blessed the object created
if (not $self->_validate_parameters()) {
croak qq(Invalid parameters passed in class $class);
}
return $self #This is the object you created.
}