Z Moose, można mieć lazy
builders
atrybutów, gdzie konstruktor jest wywoływana, gdy atrybut jest pierwszy dostępny jeżeli atrybut nie została już wypełniona. Możesz mieć typ przymusu atrybutu z coerce
, ale jest on stosowany , gdy atrybut jest ustawiony, więc nawet przy inicjalizacji obiektu.Lazy Atrybut Przymus
szukam sposobu realizacji leniwe przymus, gdzie atrybut może być początkowo zaludniony, ale przymusowej tylko wtedy, gdy po raz pierwszy dostępne. Jest to ważne, gdy przymus jest drogi.
W poniższym przykładzie, używam Union Typ i sposób modyfikatory, aby to zrobić:
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";
Jednakże istnieje kilka problemów z tym:
lubię typ związkową + modyfikator metody podejście. Jest sprzeczne z sugestią "najlepszych praktyk" do use coercion instead of unions. To nie jest deklaratywne.
muszę to zrobić z wielu atrybutów całej wielu klas. Dlatego potrzebna jest pewna forma suszenia. Mogą to być role atrybutów meta-atrybutów, typ-koercja, co ty.
Aktualizacja: Śledziłem ikegami's sugestię do hermetyzacji drogiego typu przymus wewnątrz obiektu i zapewnić zewnętrzny przymus do tego obiektu:
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;
To działa, jeśli $foo->x->value
nazywa. Nie rozwiązuje to jednak punktu nr 2, ponieważ musiałbym utworzyć podtyp My::ArrayFromInt
i ::Lazy
dla każdego atrybutu, który chciałbym przekształcić. A jeśli to możliwe, chciałbym unikać wywoływania $foo->x->value
.
Jeśli istnieją dwa sposoby reprezentacji układu odniesienia, powinno być możliwe uzyskanie reprezentacji. Przymocuj do obiektu, a następnie pobierz dane z obiektu w wybranym formacie. [Przykład] (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 Problem polega na tym, że przymus jest drogi; Chcę go wykonać tylko wtedy, gdy żądany jest atrybut. – devoid