2010-02-15 13 views
12

Simplemente odio cómo el acceso de CGI::Application para el objeto CGI se llama query.En Perl, ¿cuál es la forma correcta para que una subclase haga alias un método en la clase base?

Me gustaría que mis clases de instancia puedan usar un acceso de acceso llamado cgi para obtener el objeto CGI asociado con la instancia actual de mi subclase CGI::Application.

Aquí está un ejemplo de auto-contenido de lo que estoy haciendo:

package My::Hello; 

sub hello { 
    my $self =shift; 
    print "Hello @_\n"; 
} 

package My::Merhaba; 

use base 'My::Hello'; 

sub merhaba { 
    goto sub { shift->hello(@_) }; 
} 

package main; 

My::Merhaba->merhaba('StackOverflow'); 

Esto está funcionando, ya que creo que debería y no puedo ver ningún problema (por ejemplo, si quería heredar de My::Merhaba: Las subclases no necesita saber nada sobre merhaba).

¿Hubiera sido mejor/más correcto escribir

sub merhaba { 
    my $self = shift; 
    return $self->hello(@_); 
} 

¿Cuáles son las ventajas/desventajas de utilizar goto &NAME a los efectos de aliasing un nombre de método? ¿Hay una mejor manera?

Nota: Si usted tiene una necesidad de responder con goto es malo no lo hacen debido a este uso de goto de Perl es diferente de lo que tiene en mente.

+0

No veo el sentido de usar 'goto & NAME' de esta manera. El contenedor 'sub {}' agrega un nuevo marco a la pila de llamadas. También puede llamar al método directamente. –

+0

@Michael Carman usa 'can' como en la primera parte de la respuesta de @Eric Strom elimina ese problema, creo. –

+0

Tenía curiosidad sobre cualquier progreso con 'Method :: Alias'. –

Respuesta

10

Su enfoque con goto es el correcto, ya que garantizará que caller/wantarray y similares sigan funcionando correctamente.

lo haría configuración del nuevo método como este:

sub merhaba { 
    if (my $method = eval {$_[0]->can('hello')}) { 
     goto &$method 
    } else { 
     # error code here 
    } 
} 

O si no desea utilizar la herencia, se puede añadir el nuevo método para el paquete existente de su Código de llamada:

*My::Hello::merhaba = \&My::Hello::hello; 
    # or you can use = My::Hello->can('hello'); 

entonces se puede llamar:

My::Hello->merhaba('StackOverflow'); 

y obtener el resultado deseado.

De cualquier forma funcionaría, la ruta de herencia es más fácil de mantener, pero agregar el método al paquete existente daría lugar a llamadas de método más rápidas.

Editar:

Como se señaló en los comentarios, hay algunos casos fueron la asignación pegote se ejecutará en conflicto con la herencia, por lo que en caso de duda, utilice el primer método (la creación de un nuevo método en un paquete de sub)

Michael Carman sugiere la combinación de ambas técnicas en una función de auto redefinición:

sub merhaba { 
    if (my $method = eval { $_[0]->can('hello') }) { 
     no warnings 'redefine'; 
     *merhaba = $method; 
     goto &merhaba; 
    } 
    die "Can't make 'merhaba' an alias for 'hello'"; 
} 
+0

¿habría casos de herencia que '* My :: Hello :: merhaba = My :: Hello-> can ('hello')' no funcionaría? Cualquier subclase vería Merhaba como un método apropiado, e incluso si 'hello' se manejara por herencia en el paquete padre, aún encontraría el método correcto. No funcionaría si 'hello' se sirvió con un autocargador, y el autocargador mantuvo algún tipo de estado (no era puro en un sentido funcional). –

+0

+1 Buen trabajo. Lo siento, publiqué una respuesta de la competencia. Simplemente no entendí cómo 'llamador' /' wantarray' relacionado con la pregunta, al principio. –

+0

Storm '* My :: Hello :: merhaba = My :: Hello-> can ('hello')' no hará lo correcto si alguna de las subclases anula 'hello'. No sé si hay alguna forma de lograr que la asignación global funcione correctamente con la herencia. Preferiría aceptar una respuesta que no se desvíe a un territorio problemático. –

3

Puede asignar un alias a las subrutinas mediante la manipulación de la tabla de símbolos:

*My::Merhaba::merhaba = \&My::Hello::hello; 

Algunos ejemplos se pueden encontrar here.

+1

Eso siempre invocaría 'My :: Hello :: hello' independientemente de la herencia. –

2

No estoy seguro de lo que es la manera correcta, pero Adam Kennedy utiliza el segundo método (es decir, sin goto) en Method::Alias (click here to go directly to the source code)

+0

Gracias. Eso me ayudó a entender la respuesta de Eric Strom. Justo antes de la cita que mencionas, Adam dice: * "Ten en cuenta que esto agrega una entrada adicional a la matriz de llamadas, pero esto no es realmente tan importante a menos que seas paranoico acerca de estas cosas." * Entonces, esto significa que usar ' goto' es mejor porque evita agregar una "entrada adicional a la matriz de llamadas" *. Esto interferiría con 'wantarray' (como mencionó Eric Strom) porque cambia la percepción de lo que el método real "quiere". ¿Está bien? –

+1

No creo que 'wantarray' se vea afectado; el contexto se debe propagar correctamente en cualquier caso, siempre que la llamada sea la última instrucción en el contenedor. es decir, no hace algo como 'my @results = $ self-> method(); return @ results'. El uso de 'goto' ocultaría el marco de llamada, lo que es útil si el método utiliza' calller' (improbable) o cosas como 'carp' o' croak' (más probable). –

+1

@molecules => suponiendo que se llama al método en el contexto de devolución apropiado (no asignado a una variable y luego devuelto o algo similar (el ejemplo de Sinan está bien)), ese contexto se propagará hacia delante independientemente de los fotogramas de pila adicionales, I lo mencioné en mi respuesta simplemente para señalar que 'goto' se ocupa de ambos –

1

Esto es una especie de combinación de Quick-n-Dirty con un mínimo de direccionamiento indirecto usando UNIVERSAL::can.

package My::Merhaba; 
use base 'My::Hello'; 
# ... 
*merhaba = __PACKAGE__->can('hello'); 

y que tendrá un sub llamado "merhaba" en este paquete que los alias My::Hello::hello. Simplemente está diciendo que, sea lo que sea que haga este paquete bajo el nombre hello, puede hacerlo bajo el nombre merhaba.

Sin embargo, esto es insuficiente en la posibilidad de que un decorador de código pueda cambiar el sub que señala *My::Hello::hello{CODE}. En ese caso, Method::Alias podría ser la forma adecuada de especificar un método, como lo sugieren las moléculas.

Sin embargo, si se trata de una biblioteca bien controlada donde se controlan las categorías padre e hijo, entonces el método anterior es slimmmer.

Cuestiones relacionadas