В Moose, как мне изменить атрибут в любое время, когда он установлен? - PullRequest
19 голосов
/ 12 сентября 2009

Если у вас есть атрибут, который необходимо изменить в любое время, когда он установлен, есть ли удобный способ сделать так, чтобы вы сами не написали аксессор и не перелистывали содержимое $self, как в этом примере

package Foo;
use Moose;

has 'bar' => (
    isa => 'Str',
    reader => 'get_bar',
);

sub set_bar {
    my ($self, $bar) = @_;
    $self->{bar} = "modified: $bar";
}

Я считал trigger, но, похоже, требовал того же подхода.

Работает ли прямая ссылка на хэш в $self считается плохой практикой в ​​ Moose , или я беспокоюсь о проблеме?

Ответы [ 4 ]

10 голосов
/ 13 сентября 2009

Вы можете использовать модификатор метода «вокруг». Примерно так:

has 'bar' => (
    isa    => 'Str',
    reader => 'get_bar',
    writer => 'set_bar'
);

around 'set_bar' => sub {
    my ($next, $self, $bar) = @_;
    $self->$next( "Modified: $bar" );
};

И да, работа напрямую со значениями хеш-функции считается плохой практикой.

Также, пожалуйста, не думайте, что представленный мной вариант обязательно является правильным. Использование подтипов и принуждение в большинстве случаев будет правильным решением - если вы подумаете о параметре с точки зрения типа, который может быть повторно использован в вашем приложении, это приведет к гораздо лучшему проектированию, чем произвольные модификации, сделано с использованием «вокруг». Смотрите ответ от @ daotoad.

8 голосов
/ 13 сентября 2009

Я не уверен, какая модификация вам нужна, но вы могли бы добиться того, что вам нужно, с помощью приведения типов:

package Foo;
use Moose;

use Moose::Util::TypeConstraints;

subtype 'ModStr' 
    => as 'Str'
    => where { /^modified: /};

coerce 'ModStr'
    => from 'Str'
    => via { "modified: $_" };

has 'bar' => ( 
    isa => 'ModStr', 
    is  => 'rw', 
    coerce => 1,
);

Если вы используете этот подход, не все значения будут изменены. Все, что проходит проверку как ModStr, будет использоваться напрямую:

my $f = Foo->new();
$f->bar('modified: bar');  # Set without modification

Эта слабость может быть в порядке или может сделать этот подход непригодным При правильных обстоятельствах это может быть даже преимуществом.

6 голосов
/ 12 сентября 2009

Я думаю, что использование ссылки на хеш вполне нормально для триггера , например:

package Foo;
use Moose;

has 'bar' => ( 
    isa => 'Str', 
    is  => 'rw', 
    trigger => sub { $_[0]->{bar} = "modified: $_[1]" },
);

Триггер также срабатывает, когда bar arg прошло с конструктором. Этого не произойдет, если вы определите свой собственный метод set_bar или модификатор метода.

re: ссылка на хеш - как правило, я думаю, что лучше придерживаться установки / получения атрибутов, если (как и в случае с вышеуказанным триггером) нет легкой альтернативы.

Кстати, вы можете найти это недавнее сообщение о триггерах от Ничего больше интересно.

3 голосов
/ 13 сентября 2009

Если непосредственное отношение к хешу вызывает у вас беспокойство, вы можете указать альтернативного писателя, а затем использовать его из вашего подходящего имени 'public' писателя.

package Foo;
use Moose;

has 'bar' => (
   isa => 'Str',
   reader => 'get_bar',
   writer => '_set_bar',
);

sub set_bar {
   my $self = shift;
   my @args = @_;
   # play with args;
   return $self->_set_bar(@args);
}

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

(отказ от ответственности: непроверенный код, записанный из памяти, просмотр SO на нетбуке с доступом к хлипким краям)

...