Обязан ли лось снова звонить строителю после звонка в клинер?


Я хочу извлечь элементы из списка, который хранится внутри класса Moose. Класс знает, как заполнить этот список сам. Это похоже на итератор, за исключением того, что я хочу иметь возможность сбросить итератор и начать извлекать те же элементы из этого списка, как если бы я этого еще не сделал. Я намерен призвать следующим образом:

while( my $slotlist = $class->get_next_slotlist ) {
    # do something with $slotlist
}

За исключением того, что, как я уже сказал, я, возможно, захочу повторить те же самые элементы еще раз в более позднем пункте:

$class->reset_slotlists;
while( my $slotlist = $class->get_next_slotlist ) {
    # do something with $slotlist
}

Я думал о проектировании класса в соответствии со следующей урезанной (макетной) версией:

package List;
use Moose;

has '_data' => (
    traits    => [ 'Array' ],
    is        => 'ro',
    predicate => 'has_data',
    lazy      => 1,
    builder   => '_build_data',
    clearer   => 'reset',
    handles   => {
        next => 'shift',
    },
);

sub _build_data { [ 'abc', 'def' ] }

package main;
use strict;
use warnings;
use Test::More;
use Test::Pretty;

my $list = List->new;
ok ! $list->has_data;
is $list->next, 'abc';
is $list->next, 'def';
is $list->next, undef;
is $list->next, undef;
$list->reset;
is $list->next, 'abc', 'Moose calls builder again after clearing';
is $list->next, 'def';
is $list->next, undef;
is $list->next, undef;
ok $list->has_data;

done_testing;

Когда я запускаю это, лось снова вызывает builder после вызова reset () (то есть clearer). Теперь мой вопрос: гарантировано ли такое поведение? В документации ничего не сказано.

(набирая этот вопрос, я также начал задаваться вопросом: как вы думаете, это плохой дизайн? Мне нравится итераторный интерфейс, очень элегантный на стороне вызова и простой в реализации. Но является ли этот самый вопрос признаком того, что дизайн разве это не хорошо?)

1 5

1 ответ:

Да, если атрибут lazy был очищен,то при следующем вызове метода доступа значение будет перестроено.

Очистка _data по существу похожа на выполнение delete($self->{_data}). Или это было бы, если бы объекты Moose были хешрефами, но они не хешрефы, они объекты. (На самом деле это хашрефы, но часть опыта лося заключается в том, что мы должны притворяться, что не знаем этого. Подмигивающее лицо.)

Ленивые атрибуты используют exists($self->{_data}) для того, чтобы решить, должно ли значение быть построенный.

Я не думаю, что это плохой дизайн, но если массив большой, то сохранение копии в _data, чтобы итеративно уничтожить с shift, потенциально тратит память. Вы можете просто держать счетчик, где вы находитесь в массиве, и увеличивать его каждый раз.

Update: Вы правы, что это не очень хорошо документировано.