Почему бы не переместить определение этого атрибута в роль и повторно использовать его с соответствующей параметризацией в других классах?
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, что имеет тенденцию кбыть довольно большим запахом кода.
В качестве альтернативы, вы можете просто написать подтип ArrayRef
, который принимает либо список 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',
...,
);
Я бы, вероятно, выбрал первое решение, чтобы иметь возможность выбирать на основе класса объектов, содержит ли он детей, взрослыхили что угодно.