简体   繁体   English

perl子例程参考

[英]perl subroutine reference

I have a set of fields with each field having different set of validation rules. 我有一组字段,每个字段具有一组不同的验证规则。

I have placed the subroutine reference for validating a hash-ref. 我已经放置了用于验证哈希引用的子例程引用。

Currently its in my constructor, but I want to take it out of my constructor in a private sub. 当前它在我的构造函数中,但是我想在一个私有子目录中将其从我的构造函数中删除。

I have done it as below 我做了如下

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;
}

Now I want to take out all this validation rules out of my constructor and want to do some thing like below, so that I have some better control over my validation rules based on types fields.(Say some rules are common in one set of fields and I can overwrite rules for other rules just by overwriting the values of fields.) 现在,我想从构造函数中取出所有这些验证规则,并想要执行以下操作,以便我可以基于类型字段更好地控制我的验证规则(例如,某些规则在一组字段中很常见)我可以通过覆盖字段的值来覆盖其他规则的规则。)

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;
}

But I am getting Can't use string ("") as a subroutine ref while "strict refs" in use at... Can anybody tell me why is this behavior with sub ref. 但是我得到Can't use string ("") as a subroutine ref while "strict refs" in use at...有人可以告诉我为什么这种行为带有子引用。 If I check my name key, its coming to be null or sub {DUMMY}; 如果我检查姓名键,则该键将为null或子{DUMMY};

It looks to me like you are getting close to reinventing Moose poorly. 在我看来,您正接近重塑Moose Consider using Moose instead of building something similar, but less useful. 考虑使用Moose代替构建类似但不太有用的东西。

The error message means that you are passing in a string in a place where your code expects a code reference. 该错误消息表示您在代码需要代码引用的位置传递字符串。 Get a stack trace to figure out where the error is coming from. 获取堆栈跟踪以找出错误的来源。

You can do this by using Carp::Always, overriding the $SIG{__DIE__} handler to generate a stack trace, or inserting a Carp::confess into your code. 您可以使用Carp :: Always,重写$SIG{__DIE__}处理函数以生成堆栈跟踪,或者在代码中插入Carp::confess来实现此目的。

Here's a sigdie solution, stick this in your code where it will run before your module initialization: 这是一个简单的解决方案,请将其粘贴在您的代码中,该代码将在模块初始化之前运行:

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

You may need to put it in a BEGIN block. 您可能需要将其放在BEGIN块中。

I'd really like to discourage you from taking this approach to building objects. 我真的很想劝阻您不要采用这种方法来构建对象。 You happily bless any random crap passed in to the constructor as part of your object! 您可以幸福地祝福作为对象一部分传递给构造函数的任何随机废话! You blithely reach into your object internals. 您可以轻松地接触到对象的内部。 Field validation rules *do not belong in the constructor--they belong in the attribute mutators. 字段验证规则* 属于构造函数-它们属于属性更改器。

If you must use a DIY object, clean up your practices: 如果必须使用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};

}

All this code is very nice, but you have to repeat your attribute code for every attribute. 所有这些代码都非常不错,但是您必须为每个属性重复属性代码。 This means a lot of error-prone boilerplate code. 这意味着很多容易出错的样板代码。 You can get around this issue by learning to use closures or string eval to dynamically create your methods, or you can use one of Perl's many class generation libraries such as Class::Accessor, Class::Struct, Accessor::Tiny and so forth. 您可以通过学习使用闭包或字符串eval动态创建方法来解决此问题,或者可以使用Perl的许多类生成库之一,例如Class :: Accessor,Class :: Struct,Accessor :: Tiny等。 。

Or you can learn [Moose][3]. 或者您可以学习[驼鹿] [3]。 Moose is the new(ish) object library that has been taking over Perl OOP practice. Moose是新的(ish)对象库,已经取代了Perl OOP的实践。 It provides a powerful set of features and dramatically reduces boilerplate over classical 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',
);

I haven't read everything you had, but this struck me: 我还没看完你所有的东西,但这让我很震惊:

sub new {
    my $class = shift;
    my $self  = {@_};

    $class = (ref($class)) ? ref $class : $class;
    bless($self, $class);

Normally, when you create a new object, the user doesn't pass $self as one of the objects. 通常,在创建新对象时,用户不会将$self作为对象之一传递。 That's what you're creating. 那就是你所创造的。

You usually see something like this: 您通常会看到以下内容:

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.
}

Now, $self doesn't have to be a reference to a hash as in the above example. 现在, $self不必像上面的示例那样是对哈希的引用。 It could be a reference to an array. 它可能是对数组的引用。 Or maybe to a function. 或者也许是功能。 But, it's usually a reference. 但这通常是参考。 The main point, is that the user doesn't pass in $self because that's getting created by your new subroutine. 要点是,用户不会传递$self因为它是由您的new子例程创建的。

Nor, do you have to check the value of $class since that's given when the new subroutine is called. 也不必检查$class的值,因为在调用new子例程时会给出$class的值。

If you want to do your verification in a private class (an excellent idea, by the way), you can do so after the bless : 如果您想在私人课程中进行验证(顺便说一句,这是个好主意),可以在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.
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM