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

发布于 2024-09-28 00:02:59 字数 1614 浏览 7 评论 0原文

使用 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 
}

Using Moose, is it possible to create a builder that builds multiple attributes at once?

I have a project in which the object has several 'sets' of fields - if any member of the set is requested, I want to go ahead and populate them all. My assumption is that if I need the name, I'll also need the birthdate, and since they're in the same table, it's faster to get both in one query.

I'm not sure if my question is clear enough, but hopefully some sample code will make it clear.

What I have:

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;
}

But what I want is:

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 
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

伴我心暖 2024-10-05 00:02:59

在这种情况下,当我不想像 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 表达式或您拥有的东西延迟构建的。

What I do in this case, when I don't want to have a separate object as in Ether's answer, is have a lazily built attribute for the intermediate state. So, for example:

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};
}

Repeat the same pattern as birthdate for name, etc.

Reading any of the individual attributes will try to get data from raw_row, whose lazy builder will only run the SQL once. Since your attributes are all readonly, you don't have to worry about updating any object state if one of them changes.

This pattern is useful for things like XML documents, too -- the intermediate state you save can be e.g. a DOM, with individual attributes being lazily built from XPath expressions or what-have-you.

淡淡的优雅 2024-10-05 00:02:59

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

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

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

No, an attribute builder can only return one value at a time. You could build both by having each builder set the value of the other attribute before returning, but that gets ugly pretty quickly...

However, if you generally have two pieces of data that go together in some way (e.g. coming from the same DB query as in your case), you can store these values together in one attribute as an object:

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',
);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文