2014-03-06 72 views
10

我有兩個需要昂貴計算的對象屬性,所以我希望它們很懶。它們最有效地一起計算,所以我想同時計算它們。穆斯提供了一種方法來做到這一點?在Moose中,如何使用一個方法調用設置多個默認值?

我想要的是像'默認'或'建設者',但不是返回默認值,它直接設置屬性。返回值將被忽略。

has max_things => 
    is  => 'rw', 
    isa  => 'Int', 
    lazy => 1, 
    xxxxx => '_set_maxes'; 

has max_pairs => 
    is  => 'rw', 
    isa  => 'Int', 
    lazy => 1, 
    xxxxx => '_set_maxes'; 

# Let's just assume this is an expensive calculation or the max_* 
# attributes are used rarely and a lot of objects are created. 
sub _set_maxes { 
    my $self = shift; 

    if($self->is_32_bit) { 
     $self->max_things(2**31); 
     $self->max_pairs(12345 * 2); 
    } 
    else { 
     $self->max_thing(2**63); 
     $self->max_pairs(23456 * 2); 
    } 

    return; 
} 

注:我可以寫我自己的「閱讀器」或使用「左右」,但我寧願把它的聲明,讓駝鹿做的工作。我也可以創建一個新對象來存儲配對值,但對於兩個值來說似乎是過度殺傷性的。

回答

6

我通常通過將處理這兩者在第三隱藏屬性的屬性:

has 'max_things' => (
    'is'  => "rw", 
    'isa'  => "Int", 
    'lazy' => 1, 
    'default' => sub { (shift)->_both_maxes->{'max_things'} }, 
); 

has 'max_pairs' => (
    'is'  => "rw", 
    'isa'  => "Int", 
    'lazy' => 1, 
    'default' => sub { (shift)->_both_maxes->{'max_pairs'} }, 
); 

has '_both_maxes' => (
    'is'  => "ro", 
    'isa'  => "HashRef", 
    'lazy' => 1, 
    'builder' => "_build_both_maxes", 
); 

sub _build_both_maxes { 
    my $self = shift; 

    my ($max_things, $max_pairs); 
    if($self->is_32_bit) { 
     $max_things = 2 ** 31; 
     $max_pairs = 12345 * 2; 
    } 
    else { 
     $max_things = 2 ** 63; 
     $max_pairs = 23456 * 2; 
    } 

    return { 
     'max_things' => $max_things, 
     'max_pairs' => $max_pairs, 
    }; 
} 
+0

看到您通過緩存結果'$自避免調用'_build_both_maxes'兩次 - > _ both_maxes'。一個聰明的工作。內存的兩倍並不適合我,而這個假屬性會讓下一位維護者感到困惑。 – Schwern

+0

@Schwern是的,我也不愛它,但它是我遇到的最好的解決方案。希望有一些相互競爭的答案。 – AKHolland

7

我不會說這是特別優雅,但它的工作原理...

use v5.14; 
use warnings; 

package Goose { 
    use Moose; 

    has max_things => (
     is  => 'rw', 
     isa  => 'Int', 
     lazy => 1, 
     default => sub { shift->_build_maxes->max_things }, 
    ); 

    has max_pairs => (
     is  => 'rw', 
     isa  => 'Int', 
     lazy => 1, 
     default => sub { shift->_build_maxes->max_pairs }, 
    ); 

    sub is_32_bit { 1 } 

    sub _build_maxes { 
     my $self = shift; 

     warn "Running builder..."; 

     if($self->is_32_bit) { 
      $self->max_things(2**31); 
      $self->max_pairs(12345 * 2); 
     } 
     else { 
      $self->max_thing(2**63); 
      $self->max_pairs(23456 * 2); 
     } 

     $self; # helps chaining in the defaults above 
    } 
} 

my $goose = Goose->new; 
say $goose->max_things; 
say $goose->max_pairs; 
+0

只調用一次構建器,不存儲額外的數據,並且填充代碼非常小。謝謝,這是我見過的最好的工作。 – Schwern

+0

這裏的一個小變化是從'_build_maxes'返回'$ self',所以默認的子變成'shift - > _ build_maxes-> max_things'。我覺得它更清潔。 –

+0

感謝@DiabJerius,這是更好,所以我已經將其納入我的答案。 – tobyink

3

除非他們具體需要是不同的屬性,我通常使用本機屬性特徵來「模擬」多個屬性:

has config => (
    traits => ['Hash'], 
    is => 'bare', 
    isa => 'HashRef[Str]', 
    lazy => 1, 

    # returns a hashref of key/value config pairs 
    builder => 'load_config', 

    handles => { 

    has_author => [ exists => 'author' ], 
    author  => [ get => 'author' ], 
    has_email => [ exists => 'email' ], 
    email  => [ get => 'email' ], 
    }, 
); 

通過這種方式,昂貴的構建器只需返回一個帶有「作者」和「電子郵件」鍵的hashref;該屬性將生成訪問器方法,然後看起來和感覺像個人屬性的方法。如果你需要在new()中單獨設置它們,這可能不是最好的選擇,儘管你可以使用BUILDARGS()來幫助;因人而異。

http://wps.io/2012/05/simulating-multiple-lazy-attributes/

相關問題