Как определить подпрограмму объекта Moose после ее инициализации? - PullRequest
2 голосов
/ 22 октября 2010

Как определить подпрограмму объекта Moose после ее инициализации?

Я пишу объектный модуль с использованием Moose и планирую сериализовать (nstore) созданные объекты.

Изучите следующий (упрощенный!) Пример:

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 действует по-разному в зависимости от size.Поскольку size доступен только для чтения, он остается постоянным после инициализации объекта.

Так что, предполагая, что я вызываю some_sub zillion раз, очень жаль, что мне нужно пройти через все ifs каждый раз.

Лучше сделать это один раз после инициализации объекта, затем установить some_sub как более простую функцию без if s вообще.

Но... как я могу это сделать?

ОБНОВЛЕНИЕ

Возможно, мне следует добавить атрибут lazy типа subref, который будет содержать ссылку на выбранную подпрограмму.some_sub тогда просто позвонит $self->chosen_sub->(@_).Что ты думаешь?

Ответы [ 3 ]

5 голосов
/ 22 октября 2010
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(@_);
}

В качестве бонуса calculation_method теперь также сериализуем.

0 голосов
/ 22 октября 2010

По поводу вашего обновления:

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 голосов
/ 22 октября 2010

Возможно другой случай для MooseX :: SingletonMethod ! (Извините, я читаю ваши вопросы в обратном порядке!).

Например:

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


И должно быть возможно добавить это единственное создание метода изнутри вашего класса. Взгляните на этот пост в блоге: Метод Moose Singleton: теперь без ролей! А также хотчпотч здесь

...