我知道使用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'
无关。
答案 0 :(得分:12)
我想你已经回答了自己的问题。使用builder
允许后期绑定,它可以很好地与要创建子类的角色和类一起使用。如果构建器很长,那么它也很有价值 - 我从来没有在属性定义中添加多行default
。没有真正的功能差异; default
可以轻松模拟builder
,但效果不是很好。
答案 1 :(得分:4)
适当地使用“构建器”和“默认”可以使代码更易于阅读和组织。
'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访问器,而无需触及原始方法:
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' );
这些是我使用构建器澄清我的代码的大多数方法。
答案 2 :(得分:3)
之间没有区别
default => sub { $_[0]->_build_foo }
和
builder => '_build_foo'
default
和builder
之间的主要区别在于,一个调用anon sub,另一个调用named方法。
has created_time_stamp => (
default => sub { time() },
);
与
has created_time_stamp => (
builder => '_build_created_time_stamp',
);
sub _build_created_time_stamp { time() }
使用default
可以减少代码的滚动,因为一切都在您需要的地方。我出于这个原因使用它。使用较少的打字是一种奖励。
它还会迫使您更明确地覆盖构建器。其他答案已经认为这是一个骗局,但我考虑在一个尚未构建的对象上调用虚拟方法,这仍然是一个不好的做法!这就是BUILD
的用途。