2010-02-15 5 views
12

Odio semplicemente come l'accessor CGI::Application per l'oggetto CGI si chiami query.In Perl, qual è il modo giusto per una sottoclasse di aliasare un metodo nella classe base?

Vorrei mie classi di istanza per essere in grado di utilizzare una funzione di accesso di nome cgi per ottenere l'oggetto CGI associato con l'istanza corrente della mia CGI::Application sottoclasse.

Ecco un esempio autonomo di quello che sto facendo:

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'); 

Questo funziona come penso che dovrebbe e non riesco a vedere alcun problema (ad esempio, se avessi voluto ereditare da My::Merhaba: sottoclassi non c'è bisogno di sapere nulla di merhaba).

Sarebbe stato meglio/più corretto scrivere

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

Quali sono i vantaggi/svantaggi di usare goto &NAME allo scopo di aliasing un nome di metodo? C'è un modo migliore?

Nota: Se avete una voglia di rispondere con goto è il male non lo fanno perché questo uso di goto di Perl è diverso da quello che avete in mente.

+0

Non vedo il punto di utilizzare 'goto & NAME' in questo modo. Il wrapper 'sub {}' aggiunge una nuova cornice allo stack delle chiamate. Potresti anche chiamare direttamente il metodo. –

+0

@Michael Carman usando 'can' come nella prima parte della risposta di @Eric Strom elimina quella questione in cui credo. –

+0

Ero solo curioso di avere qualche progresso con 'Method :: Alias'. –

risposta

10

Il tuo approccio con goto è quello giusto, perché assicurerà che caller/wantarray e simili continuino a funzionare correttamente.

avrei messa a punto del nuovo metodo come questo:

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

Oppure, se non si desidera utilizzare l'ereditarietà, è possibile aggiungere il nuovo metodo per il pacchetto esistente dal codice chiamante:

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

quindi è possibile chiamare:

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

e ottenere il risultato desiderato.

In entrambi i casi, la route di ereditarietà è più gestibile, ma l'aggiunta del metodo al pacchetto esistente comporterebbe chiamate di metodo più veloci.

Edit:

Come sottolineato nei commenti, ci sono alcuni casi sono stati l'assegnazione glob verrà eseguito afoul con l'ereditarietà, quindi in caso di dubbio, usa il primo metodo (la creazione di un nuovo metodo in un pacchetto sub).

Michael Carman ha suggerito che unisce entrambe le tecniche in una funzione di auto ridefinizione:

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

ci sarebbero casi di ereditarietà in cui '* My :: Hello :: merhaba = My :: Hello-> can ('hello')' non funziona? Qualsiasi sottoclasse vedrebbe merhaba come un metodo appropriato, e anche se 'hello' fosse gestito dall'ereditarietà nel pacchetto genitore, troverebbe comunque il metodo giusto. Non funzionerebbe se "hello" fosse servito con un autoloader e il caricatore automatico mantenesse una sorta di stato (non era puro in senso funzionale). –

+0

+1 Buon lavoro. Scusa ho postato una risposta in competizione. All'inizio non capivo come 'caller' /' wantarray' fosse collegato alla domanda. –

+0

Storm '* My :: Hello :: merhaba = My :: Hello-> can ('hello')' non farà la cosa giusta se una delle sottoclassi sostituisce 'ciao'. Non so se ci sia un modo per far funzionare la cosa del compito globale con l'ereditarietà. Preferisco accettare una risposta che non si allontani da un territorio problematico. –

3

È possibile alias i sottoprogrammi manipolando la tabella dei simboli:

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

Alcuni esempi possono essere trovati here.

+1

Questo invoca sempre 'My :: Hello :: hello' indipendentemente dall'ereditarietà. –

2

Non sono sicuro di ciò che il modo giusto è, ma Adam Kennedy usa il secondo metodo (cioè senza goto) in Method::Alias (click here to go directly to the source code).

+0

Grazie. Ciò mi ha aiutato a capire la risposta di Eric Strom. Poco prima della citazione che hai menzionato, Adam dice, * "Si noti che questo aggiunge una voce aggiuntiva alla matrice del chiamante, ma questo non è davvero così importante a meno che non si sia paranoici riguardo a queste cose." * Quindi, questo significa che si usa ' goto' è migliore perché evita di aggiungere una * "voce aggiuntiva all'array del chiamante" *. Ciò interferirebbe con 'wantarray' (come diceva Eric Strom) perché cambia la percezione di ciò che il vero metodo" vuole ". È giusto? –

+1

Non penso che 'wantarray' sia interessato; il contesto dovrebbe propagarsi correttamente in entrambi i casi fintanto che la chiamata è l'ultima dichiarazione nel wrapper. cioè non fa qualcosa come 'my @results = $ self-> method(); return @ results'. Usando 'goto' si nasconde il frame di chiamata che è utile se il metodo usa' caller' (improbabile) o cose come 'carp' o' croak' (più probabile). –

+1

@molecules => supponendo che il metodo sia chiamato nel contesto di ritorno appropriato (non assegnato a una variabile e quindi restituito o qualcosa di simile (l'esempio di Sinan va bene)), allora quel contesto si propagherà in avanti a prescindere dai frame di stack aggiuntivi, I menzionato nella mia risposta semplicemente per sottolineare che 'goto' si prende cura di entrambi –

1

Questa è una sorta di combinazione di Quick-n-Dirty con un minimo di riferimento indiretto utilizzando UNIVERSAL::can.

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

e avrete un sub chiamato "merhaba" in questo pacchetto che alias My::Hello::hello. Stai semplicemente dicendo che qualunque cosa questo pacchetto farebbe altrimenti sotto il nome hello che può fare sotto il nome merhaba.

Tuttavia, questo è insufficiente nella possibilità che alcuni decoratori di codice possano modificare il sottotitolo a cui punta *My::Hello::hello{CODE}. In tal caso, Method::Alias potrebbe essere il modo appropriato per specificare un metodo, come suggerito dalle molecole.

Tuttavia, se si tratta di una libreria piuttosto ben controllata in cui si controllano entrambe le categorie padre e figlio, il metodo sopra riportato è slimmmer.

Problemi correlati