2014-03-06 18 views
10

Ich habe zwei Objektattribute, die teure Berechnungen erfordern, also möchte ich, dass sie faul sind. Sie werden am effizientesten zusammen berechnet, daher möchte ich sie zur gleichen Zeit berechnen. Bietet Moose eine Möglichkeit, dies zu tun?In Moose, wie setze ich mehrere Standardwerte mit einem Methodenaufruf?

Was ich möchte, ist etwas wie "Standard" oder "Builder", aber anstatt den Standardwert zurückgibt setzt es direkt Attribute. Der Rückgabewert würde ignoriert werden.

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

HINWEIS: Ich konnte meine eigenen ‚Leser‘ oder verwenden Sie ‚um‘ schreiben, aber ich würde lieber halten es deklarative und lassen Moose die Arbeit machen. Ich könnte auch ein neues Objekt erstellen, nur um die gepaarten Werte zu speichern, aber es scheint für zwei Werte übertrieben zu sein.

Antwort

6

Ich behandle dies in der Regel durch Richten beide Attribute an einem dritten versteckten Attribut:

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

vermeiden Sie rufen '_build_both_maxes' zweimal durch das Ergebnis des Caching in' $ self -> _ both_maxes'. Eine clevere Arbeit. Die doppelte Erinnerung passt nicht gut zu mir, und dieses falsche Attribut wird dem nächsten Betreuer rätselhaft sein. – Schwern

+0

@Swwern Ja, ich bin auch nicht verliebt, aber es ist die beste Lösung, die ich kenne. Hoffentlich kommen einige konkurrierende Antworten dazu. – AKHolland

7

Ich würde nicht sagen, dass dies besonders elegant ist, aber es funktioniert ...

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

Ruft den Builder nur einmal auf, speichert keine zusätzlichen Daten und der Shim-Code ist ziemlich minimal. Danke, das ist die beste Arbeit, die ich bisher gesehen habe. – Schwern

+0

Eine kleine Variation davon ist, '$ self' von' _build_maxes' zurückzugeben, so dass der Standard-Sub in 'shift -> _ build_maxes-> max_things' verwandelt wird. Ich finde es optisch sauberer. –

+0

Dank @DiabJerius, das ist schöner, so habe ich es in meine Antwort aufgenommen. – tobyink

3

Es sei denn, sie Insbesondere müssen unterschiedliche Attribute verwendet werden. Im Allgemeinen verwende ich native Attributmerkmale, um mehrere Attribute zu "emulieren":

Auf diese Weise muss der teure Builder einfach einen Hashref mit den Schlüsseln "Autor" und "E-Mail" ausgeben; Das Attribut generiert Accessormethoden, die dann wie die einzelnen Attribute aussehen und sich anfühlen. Wenn Sie sie einzeln auf new() einstellen müssen, ist dies möglicherweise nicht die beste Option, obwohl Sie BUILDARGS() verwenden können, um zu helfen; YMMV.

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