Moose::Cookbook::Recipe9 - Builder methods and lazy_build |
Moose::Cookbook::Recipe9 - Builder methods and lazy_build
package BinaryTree; use Moose;
has 'node' => (is => 'rw', isa => 'Any');
has 'parent' => ( is => 'rw', isa => 'BinaryTree', predicate => 'has_parent', weak_ref => 1, );
has 'left' => ( is => 'rw', isa => 'BinaryTree', predicate => 'has_left', lazy => 1, builder => '_build_child_tree', );
has 'right' => ( is => 'rw', isa => 'BinaryTree', predicate => 'has_right', lazy => 1, builder => '_build_child_tree', );
before 'right', 'left' => sub { my ($self, $tree) = @_; $tree->parent($self) if defined $tree; };
sub _build_child_tree { my $self = shift;
return BinaryTree->new( parent => $self ); }
If you've already read the Moose::Cookbook::Recipe3 manpage, then this example
should look awfully familiar. In fact, all we've done here is replace
the attribute default
with a builder
method.
In this particular case, the default
and builder
act exactly the
same. When the left
or right
attribute is first accessed before
it has been set, Moose will call the specified builder
method to
populate the attribute.
There are some differences between default
and builder
. Because
builder
is called by name, it goes through Perl's normal
inheritance system. This means that builder methods are both
inheritable and overrideable.
For example, we might make a BinaryTree
subclass:
package TrinaryTree; use Moose;
extends 'BinaryTree';
has 'middle' => ( is => 'rw', isa => 'BinaryTree', predicate => 'has_middle', lazy => 1, builder => '_build_child_tree', );
This doesn't quite work though. If you look closely at the
_build_child_tree
method defined in BinaryTree
, you'll notice
that it hard-codes a class name. Naughty us!
Also, as a bonus, we'll pass @_
through, so subclasses can override
the method to pass additional options to the constructor.
Good object-oriented code should allow itself to be subclassed
gracefully. Let's tweak _build_child_tree
:
sub _build_child_tree { my $self = shift;
return (ref $self)->new( parent => $self, @_ ); }
Now _build_child_tree
can be gracefully inherited and overridden.
There's more to builders than just subclassing, though. The fact that builders are called by name also makes them suitable for use in a role.
package HasAnimal; use Moose::Role;
requires '_build_animal';
has 'animal' => ( is => 'ro', isa => 'Animal', lazy => 1, builder => '_build_animal', );
This role provides an animal attribute, but requires that the consumer of the role provide a builder method it.
package CatLover; use Moose;
with 'HasAnimal';
sub _build_animal { return Cat->new(); }
The lazy_build
attribute parameter can be used as sugar to specify
a whole bunch of options at once.
has 'animal' => ( is => 'ro', isa => 'Animal', lazy_build => 1, );
This is a shorthand for this:
has 'animal' => ( is => 'ro', isa => 'Animal', required => 1, lazy => 1, builder => '_build_animal', predicate => 'has_animal', clearer => 'clear_animal', );
If your attribute starts with an underscore, Moose is smart and will
do the right thing with the predicate
and clearer
, making them
both start with an underscore. The builder
method always starts
with an underscore, since you will want this to be private the vast
majority of the time.
Note that the builder
method name is created by simply taking
``_build_'' and appending the attribute name. This means that attributes
with a leading underscore like _animal
end up with a builder named
_build__animal
.
The builder
option is a more OO-friendly version of the default
functionality. It also has the property of separating out the code
into a separate well-defined method. This alone makes it valuable. It
is quite ugly to jam a long default code reference into your attribute
definition.
Here are some good rules for determining when to use builder
vs
default
.
If the default value is a simple scalar that only needs to be
calculated once (or a constant), use default
.
If the default value is an empty reference that needs to be wrapped in
a coderef like sub { [] }
, use default
.
Otherwise, use builder
.
This ensures that your classes are easily subclassable, and also helps keep crufty code out of your attribute definition blocks.
Dave Rolsky <autarch@urth.org>
Copyright 2006-2008 by Infinity Interactive, Inc.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
Moose::Cookbook::Recipe9 - Builder methods and lazy_build |