Todo este ruido es porque Moose ofece mecanismos únicos de reutilización de código y polimorfismo que además son muy fáciles de utilizar. Así que ahora además de la herencia podrás utilizar roles (también conocidos como mixins), tendrás la capacidad de alterar fácilmente el comportamiento de una clase desde un rol, podrás delegar la de una clase en otra de manerá mágica, establecer contratos, inicialización perezosa, verificación de tipos y docenas de nuevas características que te harán reflexionar sobre como has podido programar toda tu vida sin ellas.
Directo del manual de Moose un ejemplo sencillo de implementación de herencia:
1 package Point; 2 use Moose; 3 4 has 'x' => ( is => 'rw', isa => 'Int', default => 0 ); 5 has 'y' => ( is => 'rw', isa => 'Int', default => 0 ); 6 7 sub clear { 8 my $self = shift; 9 $self->x(0); 10 $self->y(0); 11 } 12 13 sub distance_sqr { 14 my ($self, $point) = @_; 15 return ($self->x - $point->x)**2 + ($self->y - $point->y)**2; 16 } 17 18 sub distance { 19 my ($self, $point) = @_; 20 return sqrt $self->distance_sqr( $point ); 21 } 22 23 1;
La línea 2 usa Moose, lo que ejecuta un compilador que transforma el programa, y que de paso activa el modo estricto (use strict) y las advertencias (use warnings).
Luego decimos que nuestro objeto tiene dos atributos (líneas 4 y 5) llamados
x
y y
, estos atributos permiten la escritura y la lectura (is => "rw") y son de enteros (isa => "Int") y tienen un valor por defecto de 0 (default => 0), estas dos líneas generan los métodos de acceso a los atributos que además verifican la validez de los datos que se asignen a dichos atributos, también crean el código necesario para incializar los valores de los atributos en 0.Luego se implementa una operación que borra el punto, es decir lo pone en (0,0) y otros que calculan la distancia y la distancia al cuadrado de este punto a otro.
Hasta ahora, puede parecer que Moose ahorra trabajo, sin embargo, siguiendo con el ejemplo veamos como sería un punto en 3 dimensiones:
1 package Point3D; 2 use Moose; 3 4 extends 'Point'; 5 6 has 'z' => ( is => 'rw', isa => 'Int', default => 0 ); 7 8 after 'clear' => sub { 9 my $self = shift; 10 $self->z(0); 11 }; 12 13 around 'distance_sqr' => sub { 14 my ($orig, $self, $point) = @_; 15 return $self->$orig($point) + ($self->z - $point->z)**2; 16 }; 17 18 1;
Hacer una subclase es realmente simple solo se dice que esta clase extiende a otra (línea 4), se agrega un nuevo atributo y se agregan los métodos. Sin embargo Moose tiene interesantes capacidades que facilitan la reutilización del código, y es aquí donde se le empiza a ver el queso a la tostada.
En vez de escribir un nuevo método
clear
que sobreescriba el heredado, podemos agregarle un comportamiento al existente, así ni siquiera tenemos que invocar el método de la super clase.En el caso de
distance_sqr
envolvemos el código de la super clase con nuestro método, lo que se parece un poco más a la manera tradicional de sobreescribir métodos para luego invocar al código de la super clase utilizando SUPER, sin embargo, estas operaciones le dan una gran potencia a un nuevo mecanismo de polimorfismo: los roles (también conocidos como mixins), del que hablaré en el próximo artículo.Los objetos creados con Moose se utilizan como cualquier otro objeto, tomando en cuenta que los parámetros del constructor ahora son estilo hash:
1 use Point3D; 2 use Modern::Perl; 3 4 my $p1 = Point3D->new; 5 my $p2 = Point3D->new( x => 1, y => 1, z => 1 ); 6 7 say "Distance: ", $p1->distance($p2);
Notese que la invocación
$p1->distance($p2)
invoca el método distance
heredado de Point, que a su vez utiliza la envoltura distance_sqr
de Point3D, que internamente invoca al método distance_sqr
de Point.