我可以在构造时设置Moose对象属性的'isa'吗?

时间:2010-11-12 13:07:29

标签: perl moose

我有一个具有以下属性的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',
 },
);

注意isa设置为'ArrayRef [Person :: Child]'。

我希望能够在创建我的对象时在Person::ChildPerson::Adult等之间进行选择。是否可以或者我必须创建除isa属性的people之外的相同对象?

(这让我想起了Java generics)。

2 个答案:

答案 0 :(得分:5)

为什么不将该属性的定义移动到角色中并重新使用它 适当的参数化,在其他类别?

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::ChildPerson::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',
    ...,
);

我可能会选择第一个解决方案,以便能够根据对象类决定它是否包含儿童,成人或其他任何内容。

答案 1 :(得分:1)

如果你喜欢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',
 },
);

现在只有实现Interfaces :: Person接口的类才能添加到'people'。