2010-10-22 2 views
2

Comment dois-je définir un sous-programme d'objet Moose après son initialisation? J'écris un module d'objet en utilisant Moose et je prévois sérialiser (nstore) les objets créés.Comment dois-je définir un sous-programme d'objet Moose après son initialisation?

Examiner l'exemple suivant (simplifié!):

package MyObj 0.001; 

use Moose; 
use namespace::autoclean; 

has 'size' => (
is  => 'ro', 
isa  => 'Int', 
required => 1, 
); 

sub some_sub { 
my ($self, @more) = @_; 
if ($self->size() < 100) # do something; 
elsif (($self->size() < 500)) # do something else; 
elsif (($self->size() < 7500)) # do something else; 
# ... 
} 

1; 

some_sub agit différemment selon size. Puisque size est en lecture seule, il reste constant après l'initialisation de l'objet. Donc, en supposant que j'appelle some_sub zillion fois, il est dommage que je doive passer par tous les if chaque fois.

Je ferais mieux de le faire une fois que l'objet a été initialisé, puis définir some_sub pour être une fonction plus simple sans if s du tout.

Mais ... comment puis-je faire cela?

MISE À JOUR

Peut-être que je devrais ajouter un attribut de type lazy subref qui contiendra une référence au sous-programme choisi. some_sub appellera alors simplement $self->chosen_sub->(@_). Qu'est-ce que tu penses?

Répondre

5
has calculation_method => (is => 'ro', lazy_build => 1, init_arg => undef); 

sub _build_calculation_method { 
    my $self = shift; 
    return '_calculate_small' if $self->size < 100; 
    return '_calculate_medium' if $self->size < 500; 
    return '_calculate_large' if $self->size < 7500; 
    return '_calculate_enormous'; 
} 

sub _calculate_small { ... } 
sub _calculate_medium { ... } 
# etc. 

sub calculate { 
    my $self = shift; 
    my $method = $self->calculation_method; 
    return $self->$method(@_); 
} 

En prime, calculation_method est maintenant sérialisable aussi.

+0

+1 yup, c'est à peu près ce que proposait ma mise à jour. Je voulais juste m'assurer que c'était logique. –

+0

Vous pouvez aussi plier la méthode 'calculate()' dans l'attribut 'calculation_method' (et renommer comme' s/_method // ') en le faisant' isa => 'CodeRef', traits => ['Code'], handles => {calculate => 'execute_method'}); '(comme l'a noté phaylon dans la réponse de draegtun). – Ether

0

Peut-être un autre cas pour MooseX::SingletonMethod! (Désolé je lis vos questions dans l'ordre inverse!).

Pour exemple:

use 5.012; 
use warnings; 

package MyObj 0.001; 
use MooseX::SingletonMethod; 
use namespace::autoclean; 

has 'size' => (
    is  => 'ro', 
    isa  => 'Int', 
    required => 1, 
); 

sub _which_sub { 
    my ($self) = @_; 

    if ($self->size < 100) { return sub{ 'A' } } 
    elsif ($self->size < 500) { return sub{ 'B' } } 
    elsif ($self->size < 7500) { return sub{ 'C' } } 
    return sub { 'D' }; 
} 


package main; 

my $obj = MyObj->new(size => 200); 

$obj->add_singleton_method(some_sub => $obj->_which_sub); 

say $obj->some_sub; # => B 


Et il devrait être possible d'ajouter cette seule création de méthode à l'intérieur de votre classe. Jetez un oeil à cet article de blog pour quelques conseils: Moose Singleton Method: Now without roles!. Et aussi un méli-mélo de messages here

+0

Je ne l'aime pas tellement, puisque je voudrais que cela soit transparent pour l'utilisateur. Cela devrait se produire lors de l'initialisation de l'objet. Voir aussi ma mise à jour pour une autre prise sur elle. –

+0

Il pourrait être fait pour être si transparent (les liens fournis donnent un aperçu de reblessing nécessaire pour cela). Cependant, si vous êtes OK avec '$ self-> chosen_sub -> (@_)' (re: votre mise à jour) alors voir ma nouvelle réponse pour cela. – draegtun

+0

Y at-il une raison pour laquelle je ne devrais pas être OK avec '$ self-> chosen_sub -> (@_)'? –

0

En ce qui concerne votre mise à jour:

use 5.012; 
use warnings; 

package MyObj; 
use Moose; 
use namespace::autoclean; 

has 'size' => (
    is  => 'ro', 
    isa  => 'Int', 
    required => 1, 
); 

has 'chosen_sub' => (
    is  => 'ro', 
    isa  => 'CodeRef', 
    lazy  => 1, 
    builder => '_build_chosen_sub', 
    init_arg => undef, # unless want option of providing anon sub at construction? 
); 

sub _build_chosen_sub { 
    my ($self) = @_; 

    if ($self->size < 100) { return sub{ 'A' } } 
    elsif ($self->size < 500) { return sub{ 'B' } } 
    elsif ($self->size < 7500) { return sub{ 'C' } } 
    return sub { 'D' }; 
} 

package main; 
my $obj = MyObj->new(size => 200); 
say $obj->chosen_sub->(); # => B 
+0

Notez que le trait d'attribut Code natif a des délégués 'execute' et 'execute_method' qui peuvent être utilisés pour configurer facilement un attribut CodeRef qui agit comme une méthode commune. – phaylon

+0

@phaylon: Cool, pas vu ça avant. Have now! ... http://search.cpan.org/dist/Moose/lib/Moose/Meta/Attribute/Native/Trait/Code.pm – draegtun

+0

Je pense que vous pouvez rendre 'chosen_sub' private (i.e.changez pour '_chosen_sub') puis ajoutez' sub mysub {my ($ self) = @_; return $ self -> _ chosen_sub() -> (@_);} '. Maintenant, nous pouvons appeler '$ obj-> mysub();'. Au second coup d'oeil, c'est ce que HDP a écrit. –

Questions connexes