perl moose

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 que foo 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 útil lazy_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 .