Moose "builder" против "default"
Я понимаю, что использование builder
позволяет подклассам легко переопределять атрибуты по умолчанию, а роли могут require
их. Это также можно выполнить с помощью default
следующим образом:
has 'foo' =>
is => 'rw',
isa => 'Str',
default => sub { $_[0]->_build_foo };
Мне интересно, есть ли дополнительные преимущества для использования builder
, о которых я не знаю? Я сам придумал:
-
builder
является декларативным, поэтому вы можете понять, что foo
создается _build_foo
-
builder
исключает оболочку подпрограммы, что делает ее немного быстрее
-
builder
позволяет использовать полезный lazy_build
.
UPDATE. Чтобы прояснить это, речь идет не о default
vs builder
вообще, а default => sub { $_[0]->_build_foo }
vs builder => '_build_foo'
.
Ответы
Ответ 1
Думаю, вы уже ответили на свой вопрос. Использование builder
допускает позднюю привязку, которая прекрасно сочетается с ролями и классами, которые предназначены для подкласса. Это также полезно, если строитель довольно длинный - я никогда не ставил default
больше, чем строку в определение атрибута. Там нет реальной функциональной разницы; default
может легко эмулировать builder
, но результат не очень красивый.
Ответ 2
Используя "построитель" и "по умолчанию", вы можете сделать код более удобным для чтения и организации.
'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)
}
Кроме того, использование построителя позволяет превращать дорогостоящие функции в memoized accessors, не касаясь оригинального метода:
sub get_things {
my $self = shift;
return +{ map { $_ => $self->price_for($_) }
$self->wodgets->calulate_expensive_things };
Рефакторинг с memoization:
has things => ( is => 'ro', lazy => 1, builder => 'get_things' );
Это большинство способов, которыми я использовал строитель для уточнения кода.
Ответ 3
Нет никакой разницы между
default => sub { $_[0]->_build_foo }
и
builder => '_build_foo'
Основное различие между default
и builder
заключается в том, что один вызывает anon sub, а другой вызывает именованный метод.
has created_time_stamp => (
default => sub { time() },
);
против
has created_time_stamp => (
builder => '_build_created_time_stamp',
);
sub _build_created_time_stamp { time() }
Использование default
уменьшает прокрутку кода, поскольку все, где вам это нужно. Я использую его по этой причине. То есть использование менее типизации является бонусом.
Это также заставляет вас быть более явным в переопределении строителя. Другие ответы рассматривали этот конфликт, но я считаю, что вызов виртуальных методов на объекте, который еще не был сконструирован, является плохой практикой! Для чего BUILD
.