Ссылка на подпрограмму Perl - PullRequest
0 голосов
/ 17 января 2011

У меня есть набор полей, каждое из которых имеет различный набор правил проверки.

Я поместил ссылку на подпрограмму для проверки хэш-ссылки.

В настоящее время она находится в моем конструкторе,но я хочу извлечь его из моего конструктора в приватной подпрограмме.

Я сделал это, как показано ниже

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... Может кто-нибудь сказать мне, почему это поведение с sub ref.Если я проверю свой ключ с именем, он станет нулевым или вложенным {DUMMY};

Ответы [ 2 ]

5 голосов
/ 17 января 2011

Мне кажется, что вы близки к переизобретению Лося плохо. Попробуйте использовать Moose вместо создания чего-то похожего, но менее полезного.

Сообщение об ошибке означает, что вы передаете строку в месте, где ваш код ожидает ссылку на код. Получите трассировку стека, чтобы выяснить, откуда исходит ошибка.

Вы можете сделать это, используя Carp :: Always, переопределив обработчик $SIG{__DIE__} для генерации трассировки стека, или вставив Carp::confess в ваш код.

Вот решение sigdie, вставьте его в свой код, где оно будет выполняться до инициализации вашего модуля:

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

Возможно, вам понадобится поместить его в блок BEGIN.

Я бы очень хотел отговорить вас от такого подхода к строительству объектов. Вы счастливо благословляете любую случайную чушь, передаваемую конструктору как часть вашего объекта! Вы беспечно проникаете в свои внутренние объекты. Правила проверки полей * do not принадлежат конструктору - они принадлежат мутаторам атрибута.

Если вы должны использовать самодельный объект, очистите свои практики:

# 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 и так далее. .

Или вы можете выучить [Лося] [3]. Moose - это новая (ish) объектная библиотека, которая переняла практику Perl OOP. Он предоставляет мощный набор функций и значительно сокращает количество шаблонов по сравнению с классическим ООП Perl:

use Moose;

type 'Foo'
    => as 'Int'
    => where {
        $_ > 23 and $_ < 42
    }
    => message 'Monkeys flew out my butt';

has 'some_attribute' => (
    is  => 'rw',
    isa => 'Foo',
);
2 голосов
/ 17 января 2011

Я не прочитал все, что у вас было, но это поразило меня:

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