perl - Moose "constructor" vs "predeterminado"
(3)
Creo que ya has respondido tu propia pregunta. El uso de builder
permite la vinculación tardía, que funciona muy bien con los roles y las clases que se pretende subclasificar. También es valioso si el constructor es bastante largo: nunca pongo un default
más de una línea en una definición de atributo. No hay una diferencia funcional real; default
puede emular fácilmente el builder
, pero el resultado no es muy bonito.
Entiendo que el uso de builder
permite a las subclases anular los valores predeterminados de los atributos y los roles pueden require
. Esto también se puede lograr usando el default
como sigue:
has ''foo'' =>
is => ''rw'',
isa => ''Str'',
default => sub { $_[0]->_build_foo };
Me pregunto si existen ventajas adicionales al usar un builder
que no conozco. He encontrado algo de mí mismo:
-
builder
es declarativo para que puedas introspectar quefoo
está construido por_build_foo
-
builder
elimina una envoltura de subrutina que la hace un poco más rápida -
builder
permite el uso del útillazy_build
.
ACTUALIZACIÓN Para aclarar, esto no se trata del builder
default
en general, sino por default => sub { $_[0]->_build_foo }
vs builder => ''_build_foo''
.
El uso de ''generador'' y ''predeterminado'' de forma adecuada puede hacer que su código sea más fácil de leer y organizar.
''constructor'' también puede ajustarse a un patrón familiar de programación donde los métodos privados comienzan con un guión bajo.
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)
}
Además, el uso de Builder le permite convertir funciones costosas en accesos memoiales sin tocar el método original:
sub get_things {
my $self = shift;
return +{ map { $_ => $self->price_for($_) }
$self->wodgets->calulate_expensive_things };
Refactor con memoria:
has things => ( is => ''ro'', lazy => 1, builder => ''get_things'' );
Esas son la mayoría de las formas en que he usado Builder para aclarar mi código.
No hay diferencia entre
default => sub { $_[0]->_build_foo }
y
builder => ''_build_foo''
La principal diferencia entre el default
y el builder
es que uno llama a un sub anon y el otro llama a un método con nombre.
has created_time_stamp => (
default => sub { time() },
);
versus
has created_time_stamp => (
builder => ''_build_created_time_stamp'',
);
sub _build_created_time_stamp { time() }
Usar el default
reduce el desplazamiento a través del código ya que todo está donde lo necesita. Lo uso por esa razón. Eso es menos tipeo es una ventaja.
También te obliga a ser más explícito sobre anular el constructor. Otras respuestas han considerado esto una estafa, pero considero que llamar a métodos virtuales sobre un objeto que ni siquiera se ha construido aún es una mala práctica. Para eso está BUILD
.