Могу ли я установить " isa " атрибута объекта Loose при построении?
У меня есть объект Loose со следующим атрибутом:
has 'people' => (
is => 'ro',
isa => 'ArrayRef[Person::Child]',
traits => ['Array'],
default => sub { [] },
handles => {
all_people => 'elements',
get_people => 'get',
push_people => 'push',
pop_people => 'pop',
count_people => 'count',
sort_people => 'sort',
grep_people => 'grep',
},
);
Примечание isa
имеет значение 'ArrayRef[человека: ребенок]'.
Я хотел бы иметь возможность выбирать между Person::Child
, Person::Adult
и т.д. при создании моего объекта. Возможно ли это, или я должен создать различные объекты, которые будут идентичны, за исключением isa
атрибута people
?
(это напоминает мнеJava generics ).
2 ответа:
Почему бы не переместить определение этого атрибута в роль и не использовать его повторно с помощью соответствующая параметризация в других классах?
package MyApp::Thingy::HasPeople; use MooseX::Role::Parameterized; parameter person_type => ( isa => 'Str', required => 1, ); role { my $person_type = shift->person_type; has 'people' => ( is => 'ro', isa => "ArrayRef[${person_type}]", traits => ['Array'], default => sub { [] }, handles => { all_people => 'elements', get_people => 'get', push_people => 'push', pop_people => 'pop', count_people => 'count', sort_people => 'sort', grep_people => 'grep', }, ); }; 1;
И где-то еще, в классах, которые действительно нуждаются в этом атрибуте
package MyApp::Thingy::WithChildren; use Moose; with 'MyApp::Thingy::HasPeople' => { person_type => 'Person::Child' }; 1;
Или
package MyApp::Thingy::WithAdults; use Moose; with 'MyApp::Thingy::HasPeople' => { person_type => 'Person::Adult' }; 1;
Таким образом, вы получите оба не поддерживать атрибут в двух местах, и не закончится вверх с объектами того же класса, но разных API, что, как правило, довольно большой кодовый запах.
В качестве альтернативы можно просто написать подтип который принимает либо список либо
Person::Child
, либоPerson::Adult
, либо любые другие типы людей, которые у вас есть, но только до тех пор, пока все элементы этого списка одного и того же рода.Я бы, наверное, выбрал первое решение, чтобы иметь возможность решать на основе класса объектов, содержит ли он детей, взрослых или что-то еще.use List::AllUtils 'all'; subtype 'PersonList', as 'ArrayRef', where { my $class = blessed $_->[0]; $_->[0]->isa('Person') && all { blessed $_ eq $class } @{ $_ }; }; has persons => ( is => 'ro', isa => 'PersonList', ..., );
Если Вам нравится Java, вам может понравиться следующее:
package Interfaces::Person; use Moose::Role; requires qw( list all attributes or methods that you require ); 1;
Подтвердите, что Person::Adult и Person::Child реализуют этот интерфейс:
package Person::Adult; ... # add at the end with qw(Interfaces::Person); 1;
И
package Person::Child; ... # add at the end with qw(Interfaces::Person); 1;
И обратно в основной класс:
package My::People; use Moose; use MooseX::Types::Moose qw( ArrayRef ); use MooseX::Types::Implements qw( Implements ); has 'people' => ( is => 'ro', isa => ArrayRef[Implements[qw(Interfaces::Person)]], traits => ['Array'], default => sub { [] }, handles => { all_people => 'elements', get_people => 'get', push_people => 'push', pop_people => 'pop', count_people => 'count', sort_people => 'sort', grep_people => 'grep', }, );
И теперь в 'people' можно добавлять только классы, реализующие интерфейсы::Person interface.