lunes, 4 de enero de 2010

Atributos con rasgos en Moose.

[Google translation]
En el articulo anterior vimos lo más básico de Moose, sin embargo una vez que comiences a conocer este sistema de OOP verás que tiene un monton de mecanismos (y conceptos) que facilitan muchas cosas, uno de los más útiles son los rasgos (traits), que permiten aplicar comportamientos a una clase o atributo con mucha facilidad.

Una característica de los rasgos en los atributos es que facilitan el uso de los mismos y permiten operaciones más eficientes.

Veamos un ejemplo, tomando la clase Point del artículo anterior, a la cual le agregaré el método translate, que traslada un punto:

23 sub translate {
24     my ($self, $x_delta, $y_delta) = @_;
25     $self->x( $self->x + $x_delta );
26     $self->y( $self->y + $y_delta );
27 }

Como puedes ver, el manejo de los atributos es fastidioso, aquí es donde los rasgos comienzan a ser útiles, en particular las coordenadas de un punto tienen rasgos numéricos, veamos como mejora el código cuando los aplicamos:

 1 package Point;
 2 use Moose;
 3 
 4 has 'x' => (
 5     traits  => ["Number"],
 6     is      => 'rw',
 7     isa     => 'Int',
 8     default => 0,
 9     handles => { translate_x => "add", }
10 );
11 
12 has 'y' => (
13     traits  => ["Number"],
14     is      => 'rw',
15     isa     => 'Int',
16     default => 0,
17     handles => { translate_y => "add", }
18 );
19 
20 sub clear {
21     my $self = shift;
22     $self->x(0);
23     $self->y(0);
24 }
25 
26 sub distance_sqr {
27     my ( $self, $point ) = @_;
28     return ( $self->x - $point->x )**2 + ( $self->y - $point->y )**2;
29 }
30 
31 sub distance {
32     my ( $self, $point ) = @_;
33     return sqrt $self->distance_sqr($point);
34 }
35 
36 sub translate {
37     my ( $self, $x_delta, $y_delta ) = @_;
38     $self->translate_x( $x_delta );
39     $self->translate_y( $y_delta );
40 }
41 
42 1;

Las líneas 5 y 13 declaran los rasgos (traits) del atributo, es una arreglo porque se pueden agregar tantos rasgos como quieras a un mismo atributo. Las líneas 9 y 17 generan métodos en la clase para representar el comportamiento del rasgo en el objeto, por ejemplo en la línea 9 se define translate_x como el método que implementa la operación add sobre el atributo x.

El rasgo "Number" provee métodos como add, sub, set y otros que puedes consultar en Moose::Meta::Attribute::Native::Trait::Number, que es la clase que los hace disponibles en Moose.

El uso de rasgos no solamente es cómodo, también es más eficiente que utilizar directamente los accesors, en el primer ejemplo se accede al valor de una coordenada, que se suma y se accede nuevamente (para escribirla), en el segundo caso translate_x suma directamente el argumento al atributo.

Estas diferencias se pueden hace más notorias si usamos tipos de datos más complejos, por ejemplo en el caso de las cadenas de caracteres una modificación mediante un accessor puede resultar muy costosa, supongamos un cadena de caracteres:

1 has text => (is => "rw", isa => "Str");

Si queremos afectar la cadena, digamos haciendo una substitución:

1 my $temp = $self->text;
2 $temp =~ s/java/perl/;
3 $self->text($temp);

Esto además de ser incomodo, poco claro y realmente feo (hasta parece Java), puede llegar a ser bastante ineficiente, sobre todo si las cadenas son largas, pues se estan copiando dos veces (cuando se obtienen y cuando se guardan).

En este caso se puede declarar el atributo con un rasgo "String" y usar la operación "replace":

 1 has 'text' => (
 2     traits  => ['String'],
 3     is      => 'rw',
 4     isa     => 'Str',
 5     default => q{},
 6     handles => {
 7         replace_text => 'replace',
 8     },
 9 );

y luego la usas así:

11 $self->replace_text( qr/java/, "perl" );

Cuando se usan arreglos y hashes los rasgos son realmente muy útiles, y lo mejor es que además te puedes hacer tus propios rasgos, aunque esto será para otro artículo.

Por ahora puedes ver los rasgos que ofrece Moose para los tipos de datos nativos de Perl leyendo la documentación de: Moose::Meta::Attribute::Native. Puede que consigas código donde se implementan los rasgos utilizando: MooseX::AttributeHelpers, sin embargo es mucho mejor (y más fácil) usar Moose::Meta::Attribute::Native que ahora es parte de Moose.

No hay comentarios:

Publicar un comentario