我有一个具有以下属性的Moose对象:
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',
},
);
Run Code Online (Sandbox Code Playgroud)
注意,它isa被设置为'ArrayRef [Person :: Child]'.
我想能够之间进行选择Person::Child,Person::Adult在创建我的对象等.那是可能的,或者我必须创建不同的对象,这将是相同的,除了isa在的people属性?
(这让我想起了Java泛型).
为什么不将该属性的定义移动到角色中并使用适当的参数化在其他类中重用它?
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;
Run Code Online (Sandbox Code Playgroud)
在其他地方,在实际需要该属性的类中
package MyApp::Thingy::WithChildren;
use Moose;
with 'MyApp::Thingy::HasPeople' => { person_type => 'Person::Child' };
1;
Run Code Online (Sandbox Code Playgroud)
要么
package MyApp::Thingy::WithAdults;
use Moose;
with 'MyApp::Thingy::HasPeople' => { person_type => 'Person::Adult' };
1;
Run Code Online (Sandbox Code Playgroud)
这样你就不会在两个地方维护属性,并且最终不会得到相同类但具有不同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',
...,
);
Run Code Online (Sandbox Code Playgroud)
我可能会选择第一个解决方案,以便能够根据对象类决定它是否包含儿童,成人或其他任何东西.