1

在 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 框架)

4

5 回答 5

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
于 2012-06-20T11:53:10.770 回答
2

您可以使用智能匹配

my @validkeys = qw(FOO BAR BAZ);
if ($argname ~~ @validkeys) {     # smart matching
    $self->{$argname} = $args{$argname};
} else { die ... } 

如果您不喜欢智能匹配运算符的晦涩难懂,您可以一起使用正则表达式

my $rx = '^' . join("|", @validkeys) . '$';
if ($argname =~ /$rx/) { ...
于 2012-06-20T11:15:33.063 回答
1

警告!未经测试的代码。

检查有效的密钥。

die "invalid args" if grep { ! /^FOO|BAR|BAZ$/ } keys %args;

存储%args

$self->{$_} = $args{$_} foreach(keys %args);
于 2012-06-20T10:59:57.983 回答
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 中做到这一点它可以有效地使用散列并且散列文字是可读的

于 2012-06-20T11:00:14.393 回答
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 的回答。

笔记:

  • 我正在部署到具有 Perl 5.8(真的)但没有 Params::Validate 的服务器。我有理由不推动升级到 5.10.x 等。

  • 对于我的具体情况,上述内容在简洁性和可读性之间取得了很好的平衡。我可以稍后添加更多验证而无需太多重构。

  • 这弥补了用于设置参数的 getter/setter 或访问器样式方法的优点之一(编译器捕获参数名称中的拼写错误,因为它是方法名称),同时更简洁。

对于其他人,上述内容将不适用,因此我接受了塞巴斯蒂安的回答,我认为这是最好的回答(YMMV)。

于 2012-06-20T15:49:46.670 回答