如何在Moose中使用单个构建器构建多个属性?

时间:2010-10-20 14:38:55

标签: perl moose

使用Moose,是否可以创建一次构建多个属性的构建器?

我有一个项目,其中对象有几个“集合”的字段 - 如果请求该集合的任何成员,我想继续并填充它们。我的假设是,如果我需要这个名字,我也需要生日,因为它们在同一个表中,所以在一个查询中获得这两个名称的速度更快。

我不确定我的问题是否足够清楚,但希望一些示例代码能够说清楚。

我有什么:

Package WidgetPerson;
use Moose;

has id => (is => 'ro', isa => 'Int' );
has name => (is => 'ro', lazy => 1, builder => '_build_name');
has birthdate => (is => 'ro', lazy => 1, builder => '_build_birthdate');
has address => (is => 'ro', lazy => 1, builder => '_build_address');

sub _build_name {
 my $self = shift;
 my ($name) = $dbh->selectrow_array("SELECT name FROM people WHERE id = ?", {}, $self->id);
 return $name;
}
sub _build_birthdate {
 my $self = shift;
 my ($date) = $dbh->selectrow_array("SELECT birthdate FROM people WHERE id = ?", {}, $self->id);
 return $date;
}
sub _build_address {
 my $self = shift;
 my ($date) = $dbh->selectrow_array("SELECT address FROM addresses WHERE person_id = ?", {}, $self->id);
 return $date;
}

但我想要的是:

has name => (is => 'ro', isa => 'Str', lazy => 1, builder => '_build_stuff');
has birthdate => (is => 'ro', isa => 'Date', lazy => 1, builder => '_build_stuff');
has address => (is => 'ro', isa => 'Address', lazy => 1, builder => '_build_address');
sub _build_stuff {
 my $self = shift;
 my ($name, $date) = $dbh->selectrow_array("SELECT name, birthdate FROM people WHERE id = ?", {}, $self->id);
 $self->name($name);
 $self->birthdate($date);
}
sub _build_address { 
 #same as before 
}

2 个答案:

答案 0 :(得分:7)

在这种情况下,当我不希望在Ether的答案中有一个单独的对象时,我做的是中间状态的延迟构建属性。所以,例如:

has raw_row => (is => 'ro', init_arg => undef, lazy => 1, builder => '_build_raw_row');
has birthdate => (is => 'ro', lazy => 1, builder => '_build_birthdate');

sub _build_raw_row {
   $dbh->selectrow_hashref(...);
}

sub _build_birthdate {
    my $self = shift;
    return $self->raw_row->{birthdate};
}

对名称等重复与birthdate相同的模式

读取任何单个属性将尝试从raw_row获取数据,其惰性构建器仅运行SQL一次。由于您的属性都是只读的,因此如果其中一个对象状态发生更改,则无需担心更新任何对象状态。

这种模式对XML文档之类的东西也很有用 - 你保存的中间状态可以是例如一个DOM,其中的各个属性是从XPath表达式或者你拥有的东西中延迟构建的。

答案 1 :(得分:5)

不,属性构建器一次只能返回一个值。您可以通过让每个构建器在返回之前设置其他属性的值来构建它们,但这很快就会变得丑陋......

然而,如果您通常有两个以某种方式组合在一起的数据(例如,来自与您的情况相同的数据库查询),您可以将这些值一起存储在一个属性中一个对象:

has birth_info => (
    is => 'ro', isa => 'MyApp::Data::BirthInfo',
    lazy => 1,
    default => sub {
         MyApp::Data::BirthInfo->new(shift->some_id)
    },
    handles => [ qw(birthdate name) ],
);

package MyApp::Data::BirthInfo;
use Moose;
has some_id => (
    is => 'ro', isa => 'Int',
    trigger => sub {
        # perhaps this object self-populates from the DB when you assign its id?
        # or use some other mechanism to load the row in an ORMish way (perhaps BUILD)
    }
);
has birthdate => (
    is => 'ro', isa => 'Str',
);
has name => (
    is => 'ro', isa => 'Str',
);