2012-05-10 18 views
5

Moose ile, nitelik hala nüfuslu olmasaydı oluşturucu niteliktir çağrılan nitelikler üzerinde lazybuilders ilk erişilen olabilir. coerce ile bir öznitelik türü baskısına sahip olabilirsiniz, ancak bu özellik, özniteliğinde ayarlandığında, uygulanır;Tembel Özellik Zorlama

lazy coercion, bir özniteliğin başlangıçta yerleştirilebildiği, ancak yalnızca ilk erişildiğinde zorlanabileceği bir yöntem arıyorum. Bu zorlama pahalı olduğunda önemlidir.

Aşağıdaki örnekte, bunu yapmak için bir birlik tipi ve yöntemi düzenleyiciler kullanın:

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

Ancak bu ile birkaç sorun vardır:

  1. Ben sendika türünü sevmediğim + yöntem değiştirici yaklaşım. use coercion instead of unions "En İyi Uygulamalar" önerisine aykırıdır. Bu beyan değil.

  2. Ben birçok sınıflar arasında birçok özelliklere sahip yapmanız gerekir. Bu nedenle, bir miktar DRY gereklidir. Bu meta-özellik rolleri, tip-zorlama, neyin var?

Güncelleme: Bir nesnenin içindeki pahalı tip zorlama saklanması ve bu nesneye bir dış zorlama sağlamak ikegami's öneriyi takip :

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 denir, bu çalışır. Ancak bu, # 2 noktasını çözmez, çünkü dönüştürmek istediğim her özellik için My::ArrayFromInt ve ::Lazy alt türünü oluşturmam gerekir. Mümkünse $foo->x->value'u aramaktan kaçınmak istiyorum.

+1

Bir referans noktasını temsil etmenin iki yolu varsa, biri ya temsilleri alabilmelidir. Bir nesneyi zorlayın, ardından verileri istediğiniz formatta getirin. [Örnek] (http://stackoverflow.com/questions/10506416/can-i-use-an-attribute-modifer-in-moose-in-a-base-class-to-handle-multiple-attri/10508753# 10508753) – ikegami

+0

s/'map {1} 1 .. $ val' /' (1) x $ val'/ – ikegami

+0

@ikegami Sorun, zorlamanın pahalı olduğu; Sadece özellik isteniyorsa onu gerçekleştirmek istiyorum. – devoid

cevap

0

Nasıl

çizgisinde nesnelerin çiftini oluşturmak için yardımcı yöntemin bir çeşit o kadar tamamlamayı sonra Mantıklı ediyorum

has _x => (
    is  => 'ro', 
    isa  => 'Int|MyArrayOfInts', 
    init_arg => 'x', 
    required => 1, 
); 

has x => (
    is => 'ro', 
    lazy => 1, 
    isa => 'MyArrayOfInts', 
    coerce => 1, 
    default => sub { $_[0]->_x }, 
); 

yapıyor, tarif çizgisinde typedef olması hakkında TargetType üzerinde iç gözlem ediyorum

has_lazily_coerced x => (
    is => 'ro', 
    isa => 'TargetType', 
); 

uncoerced gölge özellik için yasal türlerinin bir listesini almak ve sizin için niteliklerin çiftini oluşturmak için.