Spiffy - Marco de interfaz Spiffy Perl para usted
package Keen; use Spiffy -Base; field 'mirth'; const mood => ':-)'; sub happy { if ($self->mood eq ':-(') { $self->mirth(-1); print "Cheer up!"; } super; }
"Spiffy" es un marco y una metodología para realizar programación orientada a objetos (OO) en Perl. Spiffy combina las mejores partes de Exporter.pm, base.pm, mixin.pm y SUPER.pm en una clase básica mágica. Intenta solucionar todos los inconvenientes del Perl OO tradicional, de una manera limpia, directa y (quizás algún día) estándar.
Spiffy toma prestadas ideas de otros lenguajes OO como Python, Ruby, Java y Perl 6. También agrega algunos trucos propios.
Si echas un vistazo a CPAN, hay un montón de módulos relacionados con OO. Al comenzar un nuevo proyecto, debe elegir el conjunto de módulos que tenga más sentido y luego deberá utilizar esos módulos en cada una de sus clases. Spiffy, por otro lado, tiene todo lo que probablemente necesitarás en un módulo y solo necesitarás usarlo una vez en una de tus clases. Si conviertes a Spiffy.pm en la clase base de la clase más básica de tu proyecto, Spiffy pasará automáticamente toda su magia a todas tus subclases. ¡Es posible que eventualmente olvides que incluso lo estás usando!
La diferencia más sorprendente entre Spiffy y otras clases base orientadas a objetos de Perl es que tiene la capacidad de exportar cosas. Si crea una subclase de Spiffy, todas las cosas que Spiffy exporte serán exportadas automáticamente por su subclase, además de cualquier otra cosa que desee exportar. Y si alguien crea una subclase de su subclase, todas esas cosas se exportarán automáticamente, y así sucesivamente. Piense en ello como "Exportación heredada" y utiliza la sintaxis de especificación familiar Exporter.pm.
Para usar Spiffy o cualquier subclase de Spiffy como clase base de su clase, especifique el argumento -base
en el comando use
.
use MySpiffyBaseModule -base;
También puedes utilizar la use base 'MySpiffyBaseModule';
sintaxis y todo funcionará exactamente igual. La única advertencia es que Spiffy.pm ya debe estar cargado. Esto se debe a que Spiffy reconecta base.pm sobre la marcha para realizar toda la magia de Spiffy.
Spiffy tiene soporte para mixins tipo Ruby con roles tipo Perl6. Al igual que base
puedes utilizar cualquiera de las siguientes invocaciones:
use mixin 'MySpiffyBaseModule'; use MySpiffyBaseModule -mixin;
La segunda versión sólo funcionará si la clase que se está mezclando es una subclase de Spiffy. La primera versión funcionará en todos los casos, siempre que ya se haya cargado Spiffy.
Para limitar los métodos que se mezclan, utilice roles. (Pista: funcionan igual que una lista de Exportadores):
use MySpiffyBaseModule -mixin => qw(:basics x y !foo);
En Perl orientado a objetos, casi todas las subrutinas son un método. Cada método recibe el objeto que se le pasa como primer argumento. Eso significa que prácticamente todas las subrutinas comienzan con la línea:
my $self = shift;
Spiffy proporciona un mecanismo de filtro simple y opcional para insertar esa línea por usted, lo que resulta en un código más limpio. Si calcula que un método promedio tiene 10 líneas de código, ¡eso es el 10% de su código! Para activar esta opción, simplemente use la opción - Base
en lugar de la opción -base
, o agregue la opción -selfless
. Si el filtrado de fuentes le produce mareos, no utilice esta función. Personalmente, lo encuentro adictivo en mi búsqueda por escribir código impecablemente limpio y fácil de mantener.
Una característica útil de Spiffy es que exporta dos funciones: field
y const
que pueden usarse para declarar los atributos de su clase y generar automáticamente métodos de acceso para ellos. La única diferencia entre las dos funciones es que los atributos const
no se pueden modificar; por tanto, el descriptor de acceso es mucho más rápido.
Un aspecto interesante de la programación OO es cuando un método llama al mismo método desde una clase principal. Esto generalmente se conoce como llamar a un supermétodo. La facilidad de Perl para hacer esto es fea:
sub cleanup { my $self = shift; $self->scrub; $self->SUPER::cleanup(@_); }
Spiffy hace que sea muy fácil llamar a supermétodos. Simplemente usas la super
. No necesita pasarle ningún argumento porque automáticamente los pasa por usted. Aquí está la misma función con Spiffy:
sub cleanup { $self->scrub; super; }
Spiffy tiene un método especial para analizar argumentos llamado parse_arguments
, que también utiliza para analizar sus propios argumentos. Usted declara qué argumentos son booleanos (singletons) y cuáles están emparejados, con dos métodos especiales llamados boolean_arguments
y paired_arguments
. Parse arguments extrae los valores booleanos y pares y los devuelve en un hash anónimo, seguido de una lista de argumentos no coincidentes.
Finalmente, Spiffy puede exportar algunas funciones de depuración WWW
, XXX
, YYY
y ZZZ
. Cada uno de ellos produce un volcado YAML de sus argumentos. WWW advierte la salida, XXX muere con la salida, YYY imprime la salida y ZZZ confiesa la salida. Si YAML no se adapta a sus necesidades, puede cambiar todos los volcados al formato Data::Dumper con la opción -dumper
.
¡Eso es genial!
Spiffy implementa una idea completamente nueva en Perl. Módulos que actúan como clases orientadas a objetos y que también exportan funciones. Pero lleva el concepto de Exporter.pm un paso más allá; recorre todo el camino @ISA
de una clase y respeta las especificaciones de exportación de cada módulo. Dado que Spiffy recurre al módulo Exportador para hacer esto, puede usar todas las funciones sofisticadas de la interfaz que tiene Exportador, incluidas las etiquetas y la negación.
Spiffy considera que todos los argumentos que no comienzan con un guión comprenden la especificación de exportación.
package Vehicle; use Spiffy -base; our $SERIAL_NUMBER = 0; our @EXPORT = qw($SERIAL_NUMBER); our @EXPORT_BASE = qw(tire horn); package Bicycle; use Vehicle -base, '!field'; $self->inflate(tire);
En este caso, Bicycle->isa('Vehicle')
y también todas las cosas que Vehicle
y Spiffy
exportan irán a Bicycle
, excepto field
.
Exportar puede ser muy útil cuando ha diseñado un sistema con cientos de clases y desea que todas tengan acceso a algunas funciones o constantes.
or variables. Just export them in your main base class and every subclass
obtendrán las funciones que necesitan.
Puedes hacer casi todo lo que hace Exporter porque Spiffy delega el trabajo a Exporter (después de agregar algo de magia de Spiffy). Spiffy ofrece una variable @EXPORT_BASE
que es como @EXPORT
, pero solo para usos que usan -base
.
Si ha realizado mucha programación orientada a objetos en Perl, probablemente haya utilizado herencia múltiple (MI), y si ha realizado mucha MI probablemente se haya topado con problemas y dolores de cabeza extraños. Algunos lenguajes como Ruby intentan resolver problemas de MI utilizando una técnica llamada mixins. Básicamente, todas las clases de Ruby usan solo herencia única (SI) y luego combinan la funcionalidad de otros módulos si es necesario.
Se puede pensar en los mixins en un nivel simplista como importar los métodos de otra clase a su subclase. Pero desde el punto de vista de la implementación, esa no es la mejor manera de hacerlo. Spiffy hace lo que hace Ruby. Crea una clase anónima vacía, importa todo a esa clase y luego encadena la nueva clase en su ruta SI ISA. En otras palabras, si dices:
package AAA; use BBB -base; use CCC -mixin; use DDD -mixin;
Terminarás con una única cadena de herencia de clases como esta:
AAA << AAA-DDD << AAA-CCC << BBB;
AAA-DDD
y AAA-CCC
son los nombres reales de los paquetes de las clases generadas. Lo bueno de este estilo es que mezclar en CCC no afecta ningún método en AAA, y DDD tampoco entra en conflicto con AAA o CCC. Si mezcló un método en CCC que también estaba en AAA, aún puede acceder a él usando super
.
Cuando Spiffy mezcla CCC, incorpora todos los métodos en CCC que no comienzan con un guión bajo. En realidad, va más allá. Si CCC es una subclase, incorporará todos los métodos que CCC can
utilizar mediante herencia. Esto es muy poderoso, tal vez demasiado poderoso.
Para limitar lo que mezclas, Spiffy toma prestado el concepto de Roles de Perl6. Sin embargo, el término rol se usa de manera más vaga en Spiffy. Es muy parecido a una lista de importación que usa el módulo Exportador, y puede usar grupos (etiquetas) y negación. Si el primer elemento de tu lista usa negación, Spiffy comenzará con todos los métodos que tu clase mixin puede hacer.
use EEE -mixin => qw(:tools walk !run !:sharp_tools);
En este ejemplo, walk
y run
son métodos que EEE puede realizar, y tools
y sharp_tools
son roles de la clase EEE. ¿Cómo define la clase EEE estos roles? De manera muy simple define métodos llamados _role_tools
y _role_sharp_tools
que devuelven listas de más métodos. (¡Y posiblemente otros roles!) Lo interesante aquí es que, dado que los roles son solo métodos, también pueden heredarse. ¡Toma ese Perl6!
Al usar el indicador -Base
en lugar de -base
nunca necesitarás escribir la línea:
my $self = shift;
Esta declaración se agrega a cada subrutina de su clase mediante el uso de un filtro de origen. La magia es simple y rápida, por lo que hay una pequeña penalización en el rendimiento por crear código limpio a la par con Ruby y Python.
package Example; use Spiffy -Base; sub crazy { $self->nuts; } sub wacky { } sub new() { bless [], shift; }
es exactamente lo mismo que:
package Example; use Spiffy -base; use strict;use warnings; sub crazy {my $self = shift; $self->nuts; } sub wacky {my $self = shift; } sub new { bless [], shift; } ;1;
Tenga en cuenta que los pares vacíos después de la subrutina new
evitan que se agregue $self. También tenga en cuenta que el código adicional se agrega a las líneas existentes para garantizar que los números de línea no se modifiquen.
-Base
también activa los pragmas estrictos y de advertencia, y agrega ese molesto '1;' línea a su módulo.
Spiffy ahora admite métodos privados cuando se utiliza el mecanismo de filtro '-Base'. Simplemente declaras los subs con la palabra clave my
y los llamas con un '$'
delante. Como esto:
package Keen; use SomethingSpiffy -Base; # normal public method sub swell { $self->$stinky; } # private lexical method. uncallable from outside this file. my sub stinky { ... }
La función XXX es muy útil para la depuración porque puede insertarla en casi cualquier lugar y volcará sus datos en un YAML limpio y agradable. Tome la siguiente declaración:
my @stuff = grep { /keen/ } $self->find($a, $b);
Si tiene algún problema con esta declaración, puede depurarla de cualquiera de las siguientes maneras:
XXX my @stuff = grep { /keen/ } $self->find($a, $b); my @stuff = XXX grep { /keen/ } $self->find($a, $b); my @stuff = grep { /keen/ } XXX $self->find($a, $b); my @stuff = grep { /keen/ } $self->find(XXX $a, $b);
XXX es fácil de insertar y quitar. También es una tradición marcar áreas inciertas del código con XXX. Esto hará que los dumpers de depuración sean fáciles de detectar si olvida sacarlos.
WWW y YYY son buenos porque descartan sus argumentos y luego los devuelven. De esta manera puedes insertarlos en muchos lugares y seguir ejecutando el código como antes. Utilice ZZZ cuando necesite finalizar con un volcado de YAML y un seguimiento de pila completo.
Las funciones de depuración se exportan de forma predeterminada si usa la opción -base
, pero solo si ha usado previamente la opción -XXX
. Para exportar las 4 funciones utilice la etiqueta de exportación:
use SomeSpiffyModule ':XXX';
Para forzar que las funciones de depuración utilicen Data::Dumper en lugar de YAML:
use SomeSpiffyModule -dumper;
Esta sección describe las funciones que exporta Spiffy. Las funciones field
, const
, stub
y super
solo se exportan cuando usas las opciones -base
o -Base
.
campo
Define métodos de acceso para un campo de su clase:
package Example; use Spiffy -Base; field 'foo'; field bar => []; sub lalala { $self->foo(42); push @{$self->{bar}}, $self->foo; }
El primer parámetro pasado al field
es el nombre del atributo que se define. A los accesores se les puede dar un valor predeterminado opcional. Este valor se devolverá si no se ha establecido ningún valor para el campo en el objeto.
constante
const bar => 42;
La función const
es similar a <campo> excepto que es inmutable. Tampoco almacena datos en el objeto. Probablemente siempre quieras darle a una const
un valor predeterminado; de lo contrario, el método generado será algo inútil.
talón
stub 'cigar';
La función stub
genera un método que finalizará con un mensaje apropiado. La idea es que las subclases deben implementar estos métodos para que no se llamen a los métodos auxiliares.
súper
Si esta función se llama sin ningún argumento, llamará al mismo método en el que se encuentra, más arriba en el árbol ISA, pasándole todos los mismos argumentos. Si se llama con argumentos, utilizará esos argumentos con $self
al frente. En otras palabras, funciona como cabría esperar.
sub foo { super; # Same as $self->SUPER::foo(@_); super('hello'); # Same as $self->SUPER::foo('hello'); $self->bar(42); } sub new() { my $self = super; $self->init; return $self; }
super
simplemente no hará nada si no existe un método super. Finalmente, super
hace lo correcto en las subrutinas AUTOLOAD.
Esta sección enumera todos los métodos que cualquier subclase de Spiffy hereda automáticamente.
mezclando
Un método para mezclar una clase en tiempo de ejecución. Toma los mismos argumentos que use mixin ...
. Hace que la clase objetivo sea una combinación de la persona que llama.
$self->mixin('SomeClass'); $object->mixin('SomeOtherClass' => 'some_method');
parse_argumentos
Este método toma una lista de argumentos y los agrupa en pares. Permite argumentos booleanos que pueden tener o no un valor (el valor predeterminado es 1). El método devuelve una referencia hash de todos los pares como claves y valores en el hash. Todos los argumentos que no se pueden emparejar se devuelven como una lista. Aquí hay un ejemplo:
sub boolean_arguments { qw(-has_spots -is_yummy) } sub paired_arguments { qw(-name -size) } my ($pairs, @others) = $self->parse_arguments( 'red', 'white', -name => 'Ingy', -has_spots => -size => 'large', 'black', -is_yummy => 0, );
Después de esta llamada, $pairs
contendrá:
{ -name => 'Ingy', -has_spots => 1, -size => 'large', -is_yummy => 0, }
y @others
contendrán "rojo", "blanco" y "negro".
argumentos_booleanos
Devuelve la lista de argumentos que se reconocen como booleanos. Anule este método para definir su propia lista.
argumentos_pareados
Devuelve la lista de argumentos que se reconocen como emparejados. Anule este método para definir su propia lista.
Cuando use
el módulo Spiffy o una subclase del mismo, puedes pasarle una lista de argumentos. Estos argumentos se analizan utilizando el método parse_arguments
descrito anteriormente. El argumento especial -base
se utiliza para convertir el paquete actual en una subclase del módulo Spiffy que se está utilizando.
Cualquier parámetro no emparejado actúa como una lista de importación normal; al igual que los utilizados con el módulo Exportador.
La forma correcta de utilizar un módulo Spiffy como clase base es con el parámetro -base
en la declaración use
. Esto difiere de los módulos típicos en los que desea use base
.
package Something; use Spiffy::Module -base; use base 'NonSpiffy::Module';
Ahora puede resultar difícil realizar un seguimiento de qué es Spiffy y qué no. Por lo tanto, Spiffy se ha creado para funcionar con base.pm. Puedes decir:
package Something; use base 'Spiffy::Module'; use base 'NonSpiffy::Module';
use base
también es muy útil cuando su clase no es un módulo real (un archivo separado) sino simplemente un paquete en algún archivo que ya se ha cargado. base
funcionará ya sea que la clase sea un módulo o no, mientras que la sintaxis -base
no puede funcionar de esa manera, ya que use
siempre intenta cargar un módulo.
Para que Spiffy funcionara con base.pm, se jugó una mala pasada. Spiffy intercambia base::import
con su propia versión. Si los módulos base no son Spiffy, Spiffy llama a la base::import original. Si los módulos base son Spiffy, entonces Spiffy hace lo suyo.
Hay dos advertencias.
Spiffy debe cargarse primero.
Si Spiffy no está cargado y se invoca use base
en un módulo Spiffy, Spiffy morirá con un mensaje útil indicándole al autor que lea esta documentación. Esto se debe a que Spiffy necesitaba realizar el intercambio de importación de antemano.
Si recibe este error, simplemente coloque una declaración como esta al principio de su código:
use Spiffy ();
Sin mezclar
base.pm
puede aceptar varios argumentos. Y esto funciona con Spiffy siempre que todas las clases base sean Spiffy, o todas no lo sean. Si se mezclan, Spiffy morirá. En este caso, simplemente use declaraciones use base
separadas.
Spiffy es una forma maravillosa de realizar programación orientada a objetos en Perl, pero aún es un trabajo en progreso. Se agregarán cosas nuevas y es posible que se eliminen las que no funcionan bien.
Ingy döt Net <[email protected]>
Derechos de autor 2004-2014. Ingy punto neto.
Este programa es software gratuito; puedes redistribuirlo y/o modificarlo bajo los mismos términos que el propio Perl.
Ver http://www.perl.com/perl/misc/Artistic.html