Состав атрибутов класса Perl?
Предположим, у меня есть несколько ролей, каждая из которых определяет набор элементов:
package A;
use Moose::Role;
sub items () { qw/apple orange/ }
package B;
use Moose::Role;
with 'A';
sub items () { qw/watermelon/ }
package C;
use Moose::Role;
sub items () { qw/banana/ }
Предположим, я использую их в другом классе и хочу собрать все эти предметы:
package Foo;
use Moose;
with qw(B C);
sub do_something {
my $self = shift;
my @items = ???; # How can I get apple, orange, watermelon, banana here?
....
}
Одним из возможных решений является принятие MooseX::ComposedBehavior, но его POD говорит (конечно, на момент написания), что его API "не совсем стабилен", а также что "текущая реализация является чем-то вроде хака, и его следует заменить более надежным ". Таким образом, я расследую, можно ли это сделать, не полагаясь на такой "взлом".
Предупреждение: если вы читаете это в будущем, перейдите, чтобы проверить POD MooseX::ComposedBehavior (текущая версия: 0.003), потому что он мог измениться за это время. Вещи меняются быстро. Авторы CPAN выпускают новые версии. То, что "не совсем стабильно" в данный момент, может стать более стабильным в будущем. Там могут быть даже другие модули. Проверь себя.
В идеале должно быть что-то вроде: my @items = map $_->items, @ISA;
Однако это не будет работать с лося. Есть ли более приятные и надежные решения?
Обновление: я закончил с этим решением с тремя линиями:
package A;
use Moose::Role;
sub items () { qw/apple orange/ }
package B;
use Moose::Role;
with 'A';
sub items () { qw/watermelon/ }
package C;
use Moose::Role;
sub items () { qw/banana/ }
package Foo;
use Moose;
with qw(B C);
sub items () {}
sub do_something {
my $self = shift;
my @items = map $_->execute, grep $_,
map $_->get_method('items'),
$self->meta->calculate_all_roles_with_inheritance;
...
}
Обновление: так как разные люди просили меня на IRC-канале #moose, я удалил свое предыдущее утверждение, что MooseX::ComposedBehavior"не стабилен", и заменил его буквальным текстом, взятым из его POD.
Обновление: я написал модуль MooseX::Collect, который допускает следующий синтаксис:
package Foo;
use Moose;
use MooseX::Collect;
collect 'items';
with qw(B C);
sub do_something {
my $self = shift;
my @items = $self->items;
...
}
2 ответа
Вам нужно использовать around
:
package A;
use Moose::Role;
requires 'items';
around items => sub {
my ($orig, $self, @args) = @_;
return ($self->$orig(@args), qw/apple orange/);
};
package B;
use Moose::Role;
requires 'items';
with 'A'; # not required, do it if you want it
around items => sub {
my ($orig, $self, @args) = @_;
return ($self->$orig(@args), qw/watermelon/);
};
package C;
use Moose::Role;
requires 'items';
around items => sub {
my ($orig, $self, @args) = @_;
return ($self->$orig(@args), qw/banana/);
};
package Class;
use Moose;
with qw/B C/;
sub items {}
Но в целом использование классов для представления данных - это неправильно, для этого нужныэкземпляры классов. Трудно дать дальнейшие советы, так как ваш пример настолько тривиален. Что ты на самом деле пытаешься сделать?
После того, как вы указали на MooseX::ComposedBehavior
до IRC, я не совсем уверен, почему вы чувствуете, что не должны его использовать. В конце концов, это точно решит проблему, с которой вы столкнулись.
Да, он говорит, что его интерфейс может немного измениться в будущем. Тем не менее, сколько вам понадобится адаптироваться к этим небольшим изменениям? Для сравнения, как вы думаете, сколько времени вам понадобится, чтобы придумать альтернативное решение и реализовать его на самом деле? Как вы думаете, будет ли ваше решение сравниваться с MooseX::ComposedBehavior
в таких вещах, как правильность и надежность? По крайней мере, я бы не стал доверять себе, изобретая колесо, изначально изобретенное RJBS, и ожидая, что мое решение получится лучше.
Кроме того, если вы действительно сильно беспокоитесь о модуле, предупреждающем вас о возможных будущих изменениях, поработайте с его автором и помогите ему придать ему форму, которой он доволен, объявив его стабильным. Напишите еще несколько тестов для вашего конкретного варианта использования. Поговори с Рикардо, он хороший парень.