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 はまだ使用していませんが、セバスチャンの回答を受け入れました。

ノート:

  • Perl 5.8 (実際には) を搭載しているサーバーにデプロイしていますが、Params::Validate は搭載していません。5.10.x などへのアップグレードをまだ推進していない理由があります。

  • 私の特定の状況では、上記は簡潔さと読みやすさのバランスが取れています。あまりリファクタリングしなくても、後で検証を追加できます。

  • これにより、パラメーターを設定するためのゲッター/セッターまたはアクセサー スタイル メソッドの利点の 1 つが補われます (コンパイラーは、パラメーター名がメソッド名であるため、パラメーター名のタイプミスを検出します)。

他の人には上記は当てはまらないので、私はセバスチャンの答えを受け入れました。これは一般的に最高の答えだと思います(YMMV)。

于 2012-06-20T15:49:46.670 に答える