首页 文章

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

提问于
浏览
3

我有一个具有以下属性的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::Child ,_1793115等之间进行选择 . 这是可能的还是我必须创建除 people 属性的 isa 之外相同的不同对象?

(这让我想起了Java generics) .

2 回答

  • 1

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

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

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

  • 5

    如果你喜欢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;
    

    回到主要 class :

    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' .

相关问题