在Perl中,如果我想在对象构造函数中使用命名参数,如果我希望进行一些验证,我的代码似乎有点笨拙。
sub new {
my $class = shift;
my $self = {};
my %args = @_;
foreach my $argname (keys %args) {
if ($argname eq 'FOO') { $self->{$argname} = $args{$argname}; }
elsif ($argname eq 'BAR') { $self->{$argname} = $args{$argname}; }
elsif ($argname eq 'BAZ') { $self->{$argname} = $args{$argname}; }
…
else { die "illegal argument $argname\n"; }
}
bless $self;
return $self;
}
首先,有一个临时哈希(%args
)似乎有点笨拙。其次,整个if
链似乎冗长乏味。
后者可以简化为
if ('-FOO-BAR-BAZ-'=~m/-$argname-/) { $self->{$argname} = $args{$argname} }
else { die "..."; }
但我想这可以改进。
如果我需要检查值,仍然需要if … elsif
链?
我搜索了一下,但找不到更好的成语。有没有(除了使用某种Perl OO框架)
答案 0 :(得分:8)
我发现自己不断编写不必要的代码来检查给定的参数。但后来我发现了Params::Validate。它易于使用,如果验证失败,它提供非常清晰和用户友好的错误消息。涵盖所有可能的参数组合及其错误消息是一项繁琐的工作。我更喜欢这种方式:
use Params::Validate qw/:all/;
sub new {
my $pkg = shift;
validate(
@_, {
foo => { type => SCALAR | ARRAYREF },
bar => { type => SCALAR, optional => 1},
baz => { type => ARRAYREF, default => ['value'] },
quux => { isa => 'CGI' }
}
);
return bless { @_ }, $pkg;
}
以后这段代码
MyApp::Something->new(
foo => 123,
bbr => 'typo',
quux => CGI->new()
);
变为:
The following parameter was passed in the call to MyApp::Something::new but was not listed in the validation options: bbr
at test.pl line 14.
MyApp::Something::new(undef, 'foo', 123, 'bbr', 'typo', 'quux', 'CGI=HASH(0x7fd4fa1857e0)') called at test.pl line 27
答案 1 :(得分:2)
您可以使用smart matching
my @validkeys = qw(FOO BAR BAZ);
if ($argname ~~ @validkeys) { # smart matching
$self->{$argname} = $args{$argname};
} else { die ... }
如果您不喜欢智能匹配操作员的默默无闻,您可以一起摆动正则表达式
my $rx = '^' . join("|", @validkeys) . '$';
if ($argname =~ /$rx/) { ...
答案 2 :(得分:1)
警告!未经测试的代码。
检查有效密钥。
die "invalid args" if grep { ! /^FOO|BAR|BAZ$/ } keys %args;
存储%args
。
$self->{$_} = $args{$_} foreach(keys %args);
答案 3 :(得分:1)
进行验证,您可以定义所有合法参数的哈希值,然后只测试密钥是否在其中
例如:
my %legal = ('FOO' => 1, 'BAR' => 1, 'BAZ' => 1);
my %args = @_;
foreach my $argname (keys %args) {
if(exists $legal{$argname}) { $self->{$argname} = $args{$argname}; }
else { die "illegal argument $argname\n"; }
}
关于笨拙的事情:那就是在perl中做到这一点
它可以有效地使用散列,并且散列文字是可读的
答案 4 :(得分:0)
为了完整性我正在添加这个答案(我自己的问题),描述我实际要做的事情,这是基于几个答案的元素。
sub new {
my $package = shift;
# 1. validate argument names
my %args = @_;
my $valid = '^FOO|BAR|BAZ$';
for (keys %args) { die "invalid arg $_\n" unless /$valid/; }
# 2. construct instance from arguments
return bless { @_ };
}
虽然我还没有使用Params :: Validate,但我接受了Sebastian's answer。
注意:
我正在部署到具有Perl 5.8(真的)但不是Params :: Validate的服务器。我有理由还没有推动升级到5.10.x等。
对于我的具体情况,上述内容在简洁性和可读性之间取得了良好的平衡。我可以在没有太多重构的情况下添加更多验证。
这补偿了getter / setter或accessor样式方法的一个优点,即设置参数(编译器捕获参数名称中的拼写错误,因为它是方法名称),同时更简洁。
对于其他人,以上不适用,所以我接受了塞巴斯蒂安的答案,我觉得这是最好的答案(YMMV)。