【问题标题】:perl subroutine referenceperl 子程序参考
【发布时间】:2011-01-17 13:54:37
【问题描述】:

我有一组字段,每个字段都有不同的验证规则。

我已经放置了用于验证哈希引用的子例程引用。

目前它在我的构造函数中,但我想把它从我的构造函数中取出来。

我已经做到了如下

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... 谁能告诉我为什么这种行为与子参考。如果我检查我的姓名键,它会变为 null 或 sub {DUMMY};

【问题讨论】:

  • 这里还要补充一点,我已经这样做了Field1 => {name => sub{$self->checkField1(@_);},有时我还必须验证单个字段,并考虑调用单个字段规则,例如$obj->checkField1('string',@args); 如果有一个更好的方法准备改变我的方法
  • 这里发生了有趣的事情,它没有任何问题,实际上,我定义了从 Field1 开始的键,生成的第一个字段是 Field0 :-( .. 但是这个问题仍然有更好的方法。
  • 您将它们放入属性中的每个对象的验证实际上是否不同,或者您只是为了捕获$self 而这样做?因为你不需要...
  • @Hobbs,对于当前场景,我有两个文件,每行分别包含 50 和 60 个字段。我必须为不同的规则集验证每个字段。基于类型 1 或类型 2 文件,我将生成验证哈希,然后在循环中按顺序对每个字段应用规则。而相同的字段也出现在应用程序的其他部分(但不是一次所有字段)。我正在考虑使用相同的子例程(适用于 60 个中的 5 个)。这就是为什么我试图让它附着在对象上,我不确定这种方法是否会给我带来任何好处或损失。

标签: perl validation constructor reference subroutine


【解决方案1】:

在我看来,您差一点就要彻底改造 Moose。考虑使用Moose,而不是构建类似但不太有用的东西。

错误消息意味着您在代码需要代码引用的地方传入了一个字符串。获取堆栈跟踪以找出错误的来源。

您可以通过使用 Carp::Always、覆盖 $SIG{__DIE__} 处理程序以生成堆栈跟踪或在代码中插入 Carp::confess 来做到这一点。

这是一个 sigdie 解决方案,将其粘贴在您的代码中,它将在您的模块初始化之前运行:

$SIG{__DIE__} = sub { Carp::confess(@_) };

您可能需要将其放入 BEGIN 块中。

我真的很想劝阻您不要采用这种方法来构建对象。你很高兴地祝福任何作为对象的一部分传入构造函数的随机垃圾!你愉快地进入你的对象内部。字段验证规则*属于构造函数——它们属于属性修改器。

如果您必须使用 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 等等.

或者你可以学习 [Moose][3]。 Moose 是已经接管 Perl OOP 实践的新的(ish)对象库。它提供了一组强大的功能,并大大减少了经典 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',
);

【讨论】:

  • 感谢 daotoad,我很想使用 moose,但由于一些糟糕的不可避免的限制,我无法在这个项目中使用它:-( ...
【解决方案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.
}

【讨论】:

  • 感谢您的指正。但这里的要求更多是关于基于一个参数类型获取一组规则。假设 id 类型是女士,我想使用不同的验证规则,而对于男士,我想使用一组单独的验证规则。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-22
  • 1970-01-01
  • 1970-01-01
  • 2013-07-26
相关资源
最近更新 更多