Perl: назначить Coderef методу без взлома таблицы символов - PullRequest
2 голосов
/ 10 августа 2011

Основываясь на предыдущем запросе относительно Class :: Struct vs Object :: Accessor, я хотел бы найти лучший способ назначения подпрограммных подпрограмм из coderefs в конструкторах объектов.Эй, у меня есть атрибуты OO, поэтому давайте перейдем к методам:)

Обратите внимание, что я сказал «лучший» способ.Я уже нашел два способа сделать это, которые также кажутся популярными ответами на подобные вопросы, но они требуют обхода строгих правил и предупреждений соответственно.

Обратите внимание, что этот пример дает классному пользователю возможность переопределитьметод форматирования при строительстве.Плохой пример, но вы поняли.Чтобы превентивно опровергнуть OO-пуристы, в моем конкретном проекте это имеет больше смысла, чем когда класс-пользователь подклассирует его своей собственной переопределенной версией.

Вот две мои попытки:* А другой:

use 5.014;
use autodie;
use strict;
use warnings;

package Account {
    use base 'Object::Accessor';

    sub new {
        my ($type, %args) = @_;

        my $self = bless { }, $type;
        $self->mk_accessors(qw(
            first_name last_name age_in_years activated
        ));

        $self->first_name(  $args{first_name  } // 'Default First Name'  );
        $self->last_name(   $args{last_name   } // 'Default Last Name'   );
        $self->age_in_years($args{age_in_years} // 'Default Age in Years');
        $self->activated(   $args{activated   } // 'Default Activated'   );

        {
            # Stop skim reading and look here!

            no strict 'refs';
            *{'formatted'} = $args{formatted} || sub {
                return 'Default formatting routine';
            };
        }

        return $self;
    }
}

my $account = Account->new;
say $account->formatted;

# Output: Default formatting routine

Первый подход должен работать без предупреждений, но это не так, потому что Perl думает, что я его нигде не использую, и помечает его какпредупреждение во время выполнения.Последний не работает без обхода строгих правил, и так оно и должно быть:)

Все, что я делаю, это присваиваю метод коду, предоставленному пользователем.Это должен быть простой способ сделать это.

Обратите внимание, что определение новой подпрограммы, которая вызывает определяемый пользователем coderef, также не работает, так как из него нельзя получить доступ к лексическим% args.

В то время как наши присваивают переменные пакета, он не может выполнять переходы.

Воздержание от выхода из сада строгих правил и предупреждений, каков самый чистый способ сделать это?Черт, возможно, есть способ сделать это, используя модуль Object :: Accessor, о котором я просто не знаю.

Ответы [ 3 ]

2 голосов
/ 12 сентября 2012

Если вы хотите, чтобы «отформатированный» был настраиваемым методом для каждого объекта и не переопределял метод для всего пакета / класса (т. Е. Всех объектов) при каждом вызове конструктора (что будет делать присвоение подпрограмме пакета), выможет назначить subref объекту «закрытый» ключ и вызвать его из обычного метода:

sub new { 
    my ($type, %args) = @_;
...
    $self->{_formatted} = $args{formatted} // sub { 'default' };
}

sub formatted {
    my ($self) = shift;
    return $self->{_formatted}->($self,@_);
}
1 голос
/ 11 августа 2011

strict - это сделано , чтобы иметь возможность отключить его для конкретного кода, где вы хотите делать вещи, которые вы не хотите делать случайно. Особенно, если вы делаете это так, как вы делаете, в наименьшем возможном объеме. Он работает так же, как прагмы в C. Он позволяет вам «нарушать правила», не становясь уязвимым для множества проблем без «правил».

Однако, если вы не хотите отключать какое-либо из «правил», вам следует взглянуть на Symbol. В частности, qualify_to_ref вернет ссылку на символ, так что при приведении его обратно к символу (см. Ниже) не сработают сигналы тревоги.

Итак, для кода, который вы предоставляете, вы можете сделать:

*{ Symbol::qualify_to_ref( 'formatted' ) } 
    =  $args{formatted} 
    || sub { return 'Default formatting routine'; }
    ;
1 голос
/ 10 августа 2011

Это может быть сохранено в $ self:

$$self{formatted} = sub { ... };

Или записано в лексическом выражении:

my $formatted = sub { 1; }; # Dummy sub for initial assignment
sub new
{
    ...
    $formatted = sub { ... };
}

sub formatted { $formatted->(@_) }

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...