通过调用包装对象来创建属性默认值

时间:2015-06-11 21:58:00

标签: perl oop moose

我有一个将InnerClass对象作为属性的WrapperClass对象。 InnerClass对象具有权重属性。我的WrapperClass对象也有一个权重属性,我希望它的默认值是InnerClass对象的权重属性的值。

#!/usr/bin/perl
package InnerClass;
use Moose;

has 'weight' => (
    is => 'rw',
);

package WrapperClass;
use Moose;

has 'wrapped' => (
    is => 'rw',
    lazy => 1,
    default => sub {InnerClass->new(weight => 1)},
);

has 'weight' => (
    is => 'rw',
    default => sub {
        my $self = shift;
        $self->wrapped->weight()
    },
    lazy => 1,
);

上面的代码有效,但实际上,InnerClass有很多属性,WrapperClass需要做同样的事情。理想情况下,当我写WrapperClass时,我会做这样的事情:

use Moose;

has 'wrapped' => (
    is => 'rw',
);

my @getDefaultsFromWrappers
    = qw(weight height mass x y z label); # etc ...

foreach my $attr (@getDefaultsFromWrappers) {
    has $attr => (
        is => 'rw',
        default => sub {
            # Somehow tell the default which attribute
            # it needs to call from wrapped object?
            my $self = shift;
            $self->wrapped->???()
        },
        lazy => 1,
    );
}

但是,无法将参数传递给默认值或构建器以告知它正在构建哪个属性。我考虑过使用caller,但这看起来像是一个黑客。

有谁知道如何完成这种属性声明的方式,还是分别声明每个属性及其默认值?

2 个答案:

答案 0 :(得分:1)

您可以在问号所在地使用$attr,因为在声明属性时它仍在范围内。

foreach my $attr (@getDefaultsFromWrappers) {
    has $attr => (
        is      => 'rw',
        default => sub { shift->wrapped->$attr() },
        lazy    => 1,
    );
}

以下是一种可能的替代方法,如果属性声明不统一,您可能需要使用以下选项:

has weight => (
    is      => 'rw',
    isa     => 'Num',
    default => _build_default_sub('weight'),
    lazy    => 1,
);

has label => (
    is      => 'rw',
    isa     => 'Str',
    default => _build_default_sub('label'),
    lazy    => 1,
);

sub _build_default_sub {
    my ($attr) = @_;
    return sub { shift->wrapped->$attr };
}

答案 1 :(得分:0)

这可以通过内部对象中的方法委派和默认值来更好地处理。

有了这些,您给出的示例可以更好地写为:

#!/usr/bin/perl

use strict;
use warnings;

package InnerClass;

use Moose;

has weight => (
    is => 'rw',
    default => 1,
);

package WrapperClass;

use Moose;

has wrapped => (
    is => 'rw',
    isa => 'InnerClass',
    lazy => 1,
    default => sub { InnerClass->new },
    handles => [ 'weight' ],
);

package main;

my $foo = WrapperClass->new;

print $foo->weight;

任何其他默认值都将作为默认值添加到InnerClass上,并且在WrapperClass中,添加到包装的'句柄'数组引用表明它应该委托给该对象。

如果不希望将默认值应用于InnerClass的所有实例,则可以从那里删除默认值,指定所需的所有属性(以提供更好的错误检测),并在默认构造函数中指定所有属性