Lo que sigue es la versión en español del artículo A gentle introduction to Moose escrita por Jay Kuri en el 2009.
Perl, desde el lanzamiento de su versión 5 en 1994, tiene las características de un lenguaje orientado a objetos. Pero estas características OO siguen siendo algo así como un "hágalo Usted mismo", con el clásico estilo de Perl, ofreciendo un mínimo de apoyo integrado en el lenguaje y el resto se deja a cargo de los demás.
La principal ventaja de este estilo "hágalo Usted mismo" es que el lenguaje impone muy pocas restricciones sobre la manera de hacer las cosas. Por otro lado tiene la desventaja de ser algo intimidante con los novatos. Por lo bueno y lo malo, se ha vuelto familiar.
Otra ventaja es que las características tienen la libertad de evolucionar, por no estar sujetas por el lenguaje algunos programadores muy inteligentes tuvieron la libertad de explorar formas de hacer las cosas de maneras que no estaban preconcebidas. Moose es el resultado de ésta exploración y rápidamente se convirtió en el estandar defacto para la POO en Perl. Hoy vamos a explorar las bases de la creación y manipulación de objetos en Moose.
En la convencional Programación Orientada a Objetos en Perl hay tres cosas principales que necesitan hacerse. Estas son:
- Creación de objetos
- Atributos y métodos de acceso
- Herencia
Tomaremos un rápido exámen alrededor de estas cuestiones y las compararemos con la técnica Perl y la de Moose.
Creación de objetos
En puro Perl, la forma de crear un objeto es crear una referencia a una variable y luego "bendecirla" con una clase. Podemos hacer esto con cualquier tipo de variable, pero lo más común es usar una referencia a un hash. Esto se hace generalmente en una subrutina 'new':
package FooClass;
sub new {
my $class = shift;
my $foo = {};
bless $foo, 'FooClass';
return $foo;
}
sub new {
my $class = shift;
my $foo = {};
bless $foo, 'FooClass';
return $foo;
}
Esto devuelve un objeto. Hay algo más aún. La forma Moose de hacer esto es algo más sencilla:
package FooClass;
use Moose;
use Moose;
Eso es todo. Moose crea la subrutina new() por nosotros. Si la clase tiene atributos, Moose también se encargará de gestionarlos basándose en los argumentos que se pueden colocar.
Crear una instancia de una clase sigue la misma convención en un objeto de Moose como un objeto de puro Perl: simplemente llamar a new() en la clase. Moose, sin embargo, permite pasar los valores iniciales de sus atributos en la llamada. Por ejemplo:
FooClass->new( name => 'bob' );
Este es un poco de funcionalidad útil y gratuita que nos brinda el uso de Moose.Atributos y métodos de acceso
$foo->{name};
Este generalmente está mal visto, ya que proporciona muy poca estructura y hace fácil declarar mal accidentalmente los atributos. $foo-> {naem} puede ser un error muy difícil de encontrar. La "mejor práctica" generalmente consiste en crear las subrutinas de acceso para las variables, lo que limita a los que utilizan el objeto para trabajar con los atributos que hay creados. (Los geeks OO se refieren a esto como "encapsulación"). El problema es que en Perl OO lo debemos crearlas nosotros mismos:
sub name {
my $self = shift;
if( @_ ) {
$self->{'name'} = $_[0];
}
return $self->{'name'};
}
my $self = shift;
if( @_ ) {
$self->{'name'} = $_[0];
}
return $self->{'name'};
}
Se trata de una gran cantidad de código sólo para escribir los setter/getter del objeto. En puro Perl hay un módulo de CPAN nacido para hacer esto más fácil. Class::Accessor construye estos métodos por nosotros:
package FooClass;
use base qw(Class::Accessor);
FooClass->mk_accessors(qw(name age));
use base qw(Class::Accessor);
FooClass->mk_accessors(qw(name age));
Class::Accessor crea el método new por nosotros. Esto es claramente mejor que hacerlo a mano. Ahora es prácticamente imposible crear accidentalmente atributos o acceder a los equivocados. Hay una cosa que no hace bien. No le impide de ninguna manera poner basura en los atributos. Esto es perfectamente legal:
$foo = FooClass->new();
$foo->name(192);
$foo->age('magdalena');
$foo->name(192);
$foo->age('magdalena');
Moose, por el contrario, no sólo hace lo que Class::Accessor, sino que añade la comprobación de tipos (y algunas otras cosas). El mismo objeto creado con Moose:
package FooClass;
use Moose;
has 'name' => (
is => 'rw',
isa => 'Str'
);
has 'age' => (
is => 'rw',
isa => 'Int'
);
1;
use Moose;
has 'name' => (
is => 'rw',
isa => 'Str'
);
has 'age' => (
is => 'rw',
isa => 'Int'
);
1;
Lo anterior proporciona el mismo name() y age() que estamos acostumbrados. Pero si tratamos de establecer la edad como 'magdalena' ahora vamos a obtener un error. Por otro lado también tenemos la capacidad de decir esencialmente "este atributo no se puede cambiar después de crear el objeto". Entre otras cosas, el sistema de atributos y de control de tipos de Moose es increíblemente flexible y merece su propio tutorial dedicado.
Herencia
La herencia en programación orientada a objetos está diseñada para permitir la especialización de una clase. El ejemplo clásico es que se puede tener una clase Figura que puede proporcionar un cierto grado de funcionalidad, y cuando se necesita una clase Cuadrado se puede heredar de Figura y añadir sólo el código que hace particular al Cuadrado. En Perl "puro" OO podríamos hacer esto:
package Cuadrado;
@ISA = ("Figura");
# el resto de la clase Cuadrado
@ISA = ("Figura");
# el resto de la clase Cuadrado
Perl tiene la inteligencia para buscar los métodos que no encuentra en la clase Cuadrado en las clases definidas en @ISA. Funciona bien, pero no es muy intuitivo. Moose tiene herencia también, y lo hace más evidente diciendo:
package Cuadrado;
use Moose;
extends 'Figura';
# el resto de la clase Cuadrado
use Moose;
extends 'Figura';
# el resto de la clase Cuadrado
Bastante sencillo
Otras cosas de Moose
Moose no hace nada que no se pueda hacer con Perl OO. Moose se basa en las mismas características OO soportadas por el lenguaje Perl. Se ocupa, sin embargo, de una enorme cantidad de trabajo que de lo contrario tendríamos que hacer nosotros mismos, dándole la libertad para trabajar en su "nueva" funcionalidad en lugar de reinventar la rueda una vez más.
En este artículo he mostrado cómo hacer las cosas "normales" de Perl OO con Moose. En este punto, usted sabe cómo hacer uso de la programación orientada a objetos básica y podría comenzar a reemplazar sus enrolladas clases hechas a mano por otras construidas con Moose.
Moose, sin embargo, es más que un poco de sintaxis inteligente y algunos ahorros de tiempo. Moose tiene algunas características adicionales que lo hacen realmente increíble para trabajar con él. Sólo voy a referirme a ellas aquí, ya que requeriría un libro entero para cubrirlas adecuadamente. Algunas de las características disponibles con Moose son las siguientes:
- Metadatos de la clase: La posibilidad que tiene su código de examinar la estructura de sus objetos.
- Coerción: La capacidad de convertir los valores de un tipo a otro cuando sea necesario.
- Modificadores de métodos: La capacidad de agregar código que se ejecuta antes, después o alrededor de un método existente.
- Roles: La capacidad de agregar funcionalidad predefinida a las clases sin usar herencia.
Hay muchas otras que vale la pena investigar también. Los roles son especialmente interesantes y proporcionan una flexibilidad que no tiene precedentes en casi cualquier lenguaje orientado a objetos. También digno de mención son las extensiones de Moose que proporcionan una funcionalidad incluso más allá de Moose estándar.
Para obtener más información sobre Moose, eche un vistazo a la documentación. Para ver más en detalle Moose vs Perl OO, eche un vistazo a la versión "sin azúcar" del manual.
También puede consultar la página de inicio de Moose. Por último, he comenzado una lista de "Trampas de Moose" en Catalyzed Wiki para rastrear algunas de las partes más difíciles de Moose que he encontrado. Siéntase libre de agregar las propias, a medida que descubre los placeres de usar Moose.