Moo对象扩展顺序

时间:2019-03-07 15:21:05

标签: perl

给出以下代码,看来实例化对象的顺序很重要。当我希望每个对象都有不同的列表时,下面的代码将为两个对象打印相同的列表,因为列表是在构建时创建的实例属性。

package t;

use Moo;
use Types::Standard qw(ArrayRef);

my @list = qw/foo bar baz/;

has list => (
    is => 'rw',
    isa => ArrayRef,
    default => sub {\@list}
);

1;
---
package u;

use Moo;
use Types::Standard qw(ArrayRef);
extends 't';

sub BUILD {
    my ($self) = @_;

    push @{$self->list()}, qw/apple banana/;
    return $self;
}
1;
---
#!perl

use Data::Printer;
use t;
use u;

my $u = u->new();
p $u->list();

my $t = t->new();
p $t->list();

当前输出:

\ [
    [0] "foo",
    [1] "bar",
    [2] "baz",
    [3] "apple",
    [4] "banana"
]
\ [
    [0] "foo",
    [1] "bar",
    [2] "baz",
    [3] "apple",
    [4] "banana"
]

预期输出:

\ [
    [0] "foo",
    [1] "bar",
    [2] "baz",
    [3] "apple",
    [4] "banana"
]
\ [
    [0] "foo",
    [1] "bar",
    [2] "baz"
]

1 个答案:

答案 0 :(得分:7)

由于您要更改有问题的数组,因此您不希望引用用作默认\@list的数组,因此希望获取浅表副本[@list]

package t;

use Moo;
use Types::Standard qw(ArrayRef);

my @list = qw/foo bar baz/;

has list => (
    is => 'rw',
    isa => ArrayRef,
        builder =>
    default => sub { [@list] }
);

package u;

use Moo;
use Types::Standard qw(ArrayRef);
extends 't';

sub BUILD {
    my ($self) = @_;

    push @{$self->list()}, qw/apple banana/;
    return $self;
}

package main;

use Data::Printer;

my $u = u->new();
p $u->list();

my $t = t->new();
p $t->list();

尽管我可以使用BUILD修改属性,但不一定是最好的。您可以将类似lazy属性的内容与builder方法一起使用,然后在子类ala中重载该方法

package t;

use Moo;
use Types::Standard qw(ArrayRef);

my @list = qw/foo bar baz/;

has list => (
    is => 'rw',
    isa => ArrayRef,
    builder => '_build_list',
    lazy => 1,
);

sub _build_list {
  my $self = shift;
  return [@list];
}

package u;

use Moo;
extends 't';

sub _build_list {
  my $self = shift;
  my $list = $self->SUPER::_build_list();
  push @$list, qw/apple banana/;
  return $list;
}

package main;

use Data::Printer;

my $u = u->new();
p $u->list();

my $t = t->new();
p $t->list();