Лось "строитель" против "по умолчанию" - PullRequest
14 голосов
/ 02 марта 2012

Я понимаю, что использование builder позволяет подклассам легко переопределять атрибуты по умолчанию, а роли могут require их. Это также может быть выполнено с использованием default, например, так:

has 'foo' =>
    is       => 'rw',
    isa      => 'Str',
    default  => sub { $_[0]->_build_foo };

Мне интересно, есть ли дополнительные преимущества использования builder Я не в курсе? Я сам придумал:

  • builder декларативен, поэтому вы можете интроспектировать, что foo построен на _build_foo
  • builder устраняет обертку подпрограммы, делая ее немного быстрее
  • builder позволяет использовать полезные lazy_build.

ОБНОВЛЕНИЕ Для пояснения, речь идет не о default против builder в целом, а default => sub { $_[0]->_build_foo } против builder => '_build_foo'.

Ответы [ 3 ]

12 голосов
/ 02 марта 2012

Я думаю, что вы уже ответили на свой вопрос. Использование builder допускает позднее связывание, которое прекрасно сочетается с ролями и классами, которые предназначены для подклассов. Также полезно, если компоновщик довольно длинный - я никогда не вставляю default больше, чем строка в определение атрибута. Там нет реальной функциональной разницы; default может легко эмулировать builder, но результат не очень красивый.

4 голосов
/ 02 марта 2012

Использование 'builder' и 'default' соответствующим образом может облегчить чтение и организацию вашего кода.

'builder' также может соответствовать привычному шаблону программирования, когда частные методы начинаются с подчеркивания.

has json => ( is => 'ro', default => sub { JSON->new } )
has schema => ( is => 'ro', builder => '_schema' }

sub _schema {
  my $self = shift;
  $self->log_debug('constructing schema') if($self->debug);
  My::App::Schema->connect($self->dsn,$self->username,$self->password)  
}

Кроме того, использование компоновщика позволяет превратить дорогие функции в запомненные средства доступа, не затрагивая оригинальный метод:

sub get_things {
  my $self = shift;
  return +{ map { $_ => $self->price_for($_) }
    $self->wodgets->calulate_expensive_things };

Рефакторинг с памяткой:

has things => ( is => 'ro', lazy => 1, builder => 'get_things' );

Это большинство способов, которыми я использовал builder для уточнения своего кода.

3 голосов
/ 02 марта 2012

Нет никакой разницы между

default => sub { $_[0]->_build_foo }

и

builder => '_build_foo'

Основное различие между default и builder состоит в том, что один вызывает анон, адругие вызывают именованный метод.

has created_time_stamp => (
   default => sub { time() },
);

против

has created_time_stamp => (
   builder => '_build_created_time_stamp',
);

sub _build_created_time_stamp { time() }

Использование default уменьшает прокрутку кода, поскольку все находится там, где вам нужно.Я использую это по этой причине.То есть использование меньшего набора текста является бонусом.

Это также заставит вас быть более откровенным в отношении переопределения строителя.Другие ответы сочли это мошенничеством, но я считаю, что вызывать виртуальные методы для объекта, который еще не был создан, - плохая практика!Вот для чего BUILD.

...