2010-02-15 4 views
12

Ich hasse einfach wie CGI::Application 's Accessor für die CGI Objekt heißt query.Was ist in Perl der richtige Weg für eine Unterklasse, um eine Methode in der Basisklasse umzubenennen?

Ich möchte, dass meine Instanzklassen einen Accessor namens cgi verwenden können, um das Objekt CGI zu erhalten, das der aktuellen Instanz meiner -Unterklasse zugeordnet ist. Hier

ist ein in sich geschlossenes Beispiel dafür, was ich tue:

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

Dies funktioniert, wie ich denke, es sollte, und ich kann keine Probleme sehen (sagen wir, wenn ich von My::Merhaba erben wollte: Subklassen muss nichts über merhaba wissen).

Wäre es besser/richtigere gewesen

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

Was sind die Vorteile/Nachteile von goto &NAME von Aliasing einen Methodennamen für den Zweck verwenden zu schreiben? Gibt es einen besseren Weg?

Hinweis: Wenn Sie einen Drang zu reagieren mit goto ist böse tun es nicht, weil diese Verwendung von Perl goto anders ist als das, was Sie im Kopf haben.

+0

Ich sehe nicht den Punkt der Verwendung von 'goto & NAME' auf diese Weise. Der 'sub {}' -Wrapper fügt dem Call-Stack einen neuen Frame hinzu. Sie können die Methode auch direkt aufrufen. –

+0

@Michael Carman mit 'can' wie im ersten Teil von @Eric Strom's Antwort beseitigt dieses Problem, glaube ich. –

+0

Ich war nur neugierig auf jeden Fortschritt mit 'Methode :: Alias'. –

Antwort

10

Ihr Ansatz mit goto ist der richtige, weil es sicherstellt, dass caller/wantarray und dergleichen ordnungsgemäß funktionieren.

würde ich Setup die neue Methode wie folgt aus:

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

Oder wenn Sie die Vererbung nicht verwenden möchten, können Sie die neue Methode an das bestehende Paket von Ihrem Aufruf Code hinzu:

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

dann können Sie anrufen:

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

und das gewünschte Ergebnis erhalten.

Jeder Weg würde funktionieren, die Vererbungsroute ist wartungsfreundlicher, aber das Hinzufügen der Methode zum vorhandenen Paket würde schnellere Methodenaufrufe zur Folge haben.

Edit:

Wie in den Kommentaren darauf hingewiesen, gibt es ein paar Fälle wurden die glob Zuordnung in Konflikt geraten mit Vererbung laufen wird, also, wenn Sie Zweifel haben, verwenden Sie die erste Methode (ein neues Verfahren in einem Teilpaket zu schaffen).

Michael Carman vorgeschlagen, beide Techniken in eine selbst Neudefinition Funktion kombiniert:

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

würde es irgendwelche Vererbungsfälle geben, die '* Mein :: Hallo :: merhaba = Mein :: Hallo -> kann ('hallo')' würde nicht mit arbeiten? Jede Unterklasse würde merhaba als eine geeignete Methode sehen, und selbst wenn hallo durch Vererbung im übergeordneten Paket behandelt würde, würde es immer noch die richtige Methode finden. Es würde nicht funktionieren, wenn 'Hallo' mit einem Autoloader bedient wurde, und der Autoloader einen Zustand beibehalten würde (war im funktionalen Sinn nicht rein). –

+0

+1 Gute Arbeit. Entschuldigung, ich habe eine konkurrierende Antwort gepostet. Ich habe zuerst nicht verstanden, wie 'caller' /' wantarray' mit der Frage verwandt ist. –

+0

Storm '* Mein :: Hallo :: merhaba = Mein :: Hallo -> kann ('hallo')' nicht das Richtige tun, wenn eine der Unterklassen 'Hallo' überschreibt. Ich weiß nicht, ob es irgendeinen Weg gibt, die Glob-Zuweisungs-Sache richtig mit der Vererbung zu arbeiten. Ich würde eher eine Antwort akzeptieren, die sich nicht in problematisches Territorium verlagert. –

3

Sie können die Subroutinen alias durch die Symboltabelle zu manipulieren:

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

Einige Beispiele here gefunden werden können.

+1

Das würde immer 'My :: Hello :: Hallo' unabhängig von der Vererbung aufrufen. –

2

Ich bin nicht sicher, was der richtige Weg ist, aber Adam Kennedy verwendet Ihre zweite Methode (dh ohne goto) in Method::Alias (click here to go directly to the source code).

+0

Danke. Das hat mir geholfen, Eric Strom's Antwort zu verstehen. Kurz vor dem Zitat, das Sie erwähnten, sagt Adam: * "Beachten Sie, dass dies dem Aufrufer-Array einen zusätzlichen Eintrag hinzufügt, aber das ist nicht wirklich wichtig, es sei denn, Sie sind paranoid in Bezug auf diese Dinge." * Also bedeutet dies, ' goto "ist besser, weil es vermeidet, dass ein *" extra Eintrag zum Aufrufer-Array "* hinzugefügt wird. Dies würde mit "wantarray" (wie Eric Strom erwähnt) interferieren, weil es die Wahrnehmung dessen ändert, was die echte Methode "will". Ist das richtig? –

+1

Ich glaube nicht, dass "wantarray" betroffen ist; Der Kontext sollte in beiden Fällen korrekt übertragen werden, solange der Aufruf die letzte Anweisung im Wrapper ist. d. h. es macht nicht so etwas wie 'my @results = $ self> method(); Rückgabe @ Ergebnisse'. Die Verwendung von 'goto' würde den Aufrufrahmen ausblenden, was hilfreich ist, wenn die Methode" Anrufer "(unwahrscheinlich) oder Dinge wie" Karpfen "oder" Krächzen "(wahrscheinlicher) verwendet. –

+1

@molecules => Angenommen, die Methode wird im richtigen Rückgabekontext aufgerufen (nicht einer Variablen zugewiesen und dann zurückgegeben oder sonstwie (Sinans Beispiel ist in Ordnung)), dann wird dieser Kontext unabhängig von zusätzlichen Stack-Frames, I, propagieren erwähnte es in meiner Antwort einfach darauf hinzuweisen, dass 'goto' sich um beide kümmert –

1

Dies ist eine Art Kombination von Quick-n-Dirty mit einem Minimum an Indirection mit UNIVERSAL::can.

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

Und Sie werden ein Unter „Merhaba“ in diesem Paket, das My::Hello::hello Aliase genannt haben. Sie sagen einfach, dass was immer dieses Paket sonst unter dem Namen hello tun würde, kann es unter dem Namen merhaba tun.

Dies ist jedoch nicht ausreichend in der Möglichkeit, dass einige Code-Dekorator die Sub ändern, auf die *My::Hello::hello{CODE} verweist. In diesem Fall könnte Method::Alias der geeignete Weg sein, eine Methode zu spezifizieren, wie die Moleküle vermuten lassen.

Wenn es sich jedoch um eine ziemlich gut kontrollierte Bibliothek handelt, in der Sie sowohl die Eltern- als auch die Kindkategorie steuern, lautet die obige Methode slimmer.