隨着Moose,你可以有lazy
builders
的屬性,其中生成器調用時該屬性是第一次訪問如果屬性是不是已經填充。您可以使用coerce
對屬性進行類型強制轉換,但只要屬性設置爲,就會應用,因此即使在對象初始化時也如此。lazy屬性強制
我正在尋找一種方法來實現懶惰強制,其中一個屬性可能最初被填充,但只有當它被第一次訪問時才被強制。強制價格昂貴時,這一點很重要。
在下面的例子中,我使用的聯合類型和方法修飾符來做到這一點:
package My::Foo;
use Moose;
has x => (
is => 'rw',
isa => 'ArrayRef | Int',
required => 1
);
around "x" => sub {
my $orig = shift;
my $self = shift;
my $val = $self->$orig(@_);
unless(ref($val)) {
# Do the cocerion
$val = [ map { 1 } 1..$val ];
sleep(1); # in my case this is expensive
}
return $val;
};
1;
my $foo = My::Foo->new(x => 4);
is_deeply $foo->x, [ 1, 1, 1, 1 ], "x converted from int to array at call time";
但是有幾個問題是:
我不喜歡的聯合類型 + 方法修飾符方法。它違背了「最佳實踐」的建議use coercion instead of unions。這不是說明性的。
我需要這樣做很多屬性跨越許多類。因此需要某種形式的DRY。這可能是元屬性角色,類型強制,你有什麼。
更新: 我跟着ikegami's建議封裝對象內部的昂貴的強制類型轉換,並給該對象提供的外部強制:
package My::ArrayFromInt;
use Moose;
use Moose::Util::TypeConstraints;
subtype 'My::ArrayFromInt::Inner',
as 'ArrayRef[Int]';
coerce 'My::ArrayFromInt::Inner',
from 'Int',
via { return [ (1) x $_ ] };
has uncoerced => (is => 'rw', isa => 'Any', required => 1);
has value => (
is => 'rw',
isa => 'My::ArrayFromInt::Inner',
builder => '_buildValue',
lazy => 1,
coerce => 1
);
sub _buildValue {
my ($self) = @_;
return $self->uncoerced;
}
1;
package My::Foo;
use Moose;
use Moose::Util::TypeConstraints;
subtype 'My::ArrayFromInt::Lazy' => as class_type('My::ArrayFromInt');
coerce 'My::ArrayFromInt::Lazy',
from 'Int',
via { My::ArrayFromInt->new(uncoerced => $_) };
has x => (
is => 'rw',
isa => 'My::ArrayFromInt::Lazy',
required => 1,
coerce => 1
);
1;
這工作如果$foo->x->value
被調用。但是這並不能解決第二點問題,因爲我需要爲每個要轉換的屬性創建My::ArrayFromInt
和::Lazy
子類型。如果可能的話,我想避免撥打$foo->x->value
。
如果有兩種表示數據的方式,一個應該能夠得到任何表示。強迫進入對象,然後以所需格式從對象中獲取數據。 [實施例](http://stackoverflow.com/questions/10506416/can-i-use-an-attribute-modifer-in-moose-in-a-base-class-to-handle-multiple-attri/10508753# 10508753) – ikegami
s /'map {1} 1 .. $ val' /'(1)x $ val'/ – ikegami
@ikegami問題是強制代價很高;我只想執行它,如果該屬性被要求。 – devoid