2012-03-02 19 views
14

Entiendo que el uso de builder habilita subclases para anular los valores predeterminados de atributo fácilmente y los roles pueden require ellos. Esto también se puede lograr usando default así:Moose "generador" vs "predeterminado"

has 'foo' => 
    is  => 'rw', 
    isa  => 'Str', 
    default => sub { $_[0]->_build_foo }; 

Me pregunto si hay más ventajas de utilizar builder No estoy al tanto? Yo he llegado con un poco de mí mismo:

  • builder es declarativa para que pueda introspección que foo es construida por _build_foo
  • builder elimina un envoltorio subrutina lo que es un poco más rápido
  • builder permite el uso de la útil lazy_build.

ACTUALIZACIÓN Para aclarar, no se trata de default vs builder en general, pero default => sub { $_[0]->_build_foo } vs builder => '_build_foo'.

Respuesta

12

Creo que ya ha respondido su 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 puse 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 builder, pero el resultado no es muy bonito.

+0

¿Por qué crees que 'default => \ & builder' no hace cada una de esas cosas? – ikegami

+2

@ikegami porque '\ & builder' se resolverá en' & builder' en el paquete actual. Una subclase que proporciona un nuevo 'constructor' tendría que hacer' has '+ attr' => builder => \ & builder' de nuevo para vincularlo al método reemplazado. OTOH 'builder => 'builder'' se resuelve en tiempo de ejecución. – hobbs

+0

Como contrapunto, una subclase puede anular el atributo y cambiar el 'predeterminado'. Esto no requiere conocer el nombre del método 'constructor' probablemente privado. * Ambos * requieren saber si el atributo está usando 'default' o' builder' porque no puedes tener ambos. En general, esta información es privada y está sujeta a cambios. Anular el comportamiento predeterminado de los atributos en Moose es problemático, debe publicar detalles sobre cómo se implementa ese comportamiento predeterminado o si la subclase debe introspectarse. :/ – Schwern

3

No hay diferencia entre

default => sub { $_[0]->_build_foo } 

y

builder => '_build_foo' 

La diferencia principal entre default y builder es que uno se llama un sub anon y el otro llama a un método llamado.

has created_time_stamp => (
    default => sub { time() }, 
); 

frente

has created_time_stamp => (
    builder => '_build_created_time_stamp', 
); 

sub _build_created_time_stamp { time() } 

Usando default reduce el desplazamiento a través del código ya que todo está donde lo necesite. Lo uso por esa razón. Eso es menos tipeo es una ventaja.

También le 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. Eso es lo que BUILD es para.

+0

La pregunta es específicamente sobre 'default => sub {$ _ [0] -> _ build_foo}' vs 'builder => '_build_foo'', por lo que las subclases pueden anular el constructor predeterminado, no el constructor predeterminado en general. Es por eso que la subllamada adicional. – Schwern

+0

@Schwern, ¿por qué harías eso? No hay razón para hacer eso en absoluto. Bueno, tal vez por consistencia si has usado 'default' en cualquier otro lado. – ikegami

+2

"¿Por qué harías eso?" Es exactamente lo que estoy preguntando. Me parece que 'builder => '_build_foo'' es sintaxis sugar para' default => sub {$ _ [0] -> _ build_foo} '. ¿Hay algo más? – Schwern

4

El uso de 'generador' y 'predeterminado' de forma adecuada puede hacer que su código sea más fácil de leer y organizar.

'builder' también puede adaptarse 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 constructor le permite activar funciones caros en descriptores de acceso memoized sin tocar el método original:

sub get_things { 
    my $self = shift; 
    return +{ map { $_ => $self->price_for($_) } 
    $self->wodgets->calulate_expensive_things }; 

Refactor con memoization:

has things => (is => 'ro', lazy => 1, builder => 'get_things'); 

Esos son la mayoría de las formas He usado el generador para aclarar mi código.

+0

También me gusta usarlos para atributos en, digamos, roles, que alguien pueda querer redefinir en una clase que consuma dicho rol. Parece un poco más fácil que redefinir todo el atributo con un '+' delante del nombre. – dhoss

+0

Lo sentimos, esto no se trata de 'generador' vs' predeterminado' en general, pero 'por defecto => sub {$ _ [0] -> _ build_foo}' vs 'builder => '_build_foo''. – Schwern

Cuestiones relacionadas