2009-09-01 8 views
14

Können Sie einen Methodenaufruf in Perl abfangen, etwas mit den Argumenten tun und dann ausführen?Können Perl-Methodenaufrufe abgefangen werden?

+1

Ich sehe, dass Sie den Tag "AOP" verwendet. Fragen Sie nach bestimmten AOP-Techniken, die Sie verwenden möchten. (Die Antworten auf Ihre Frage können Ihnen helfen, ein AOP-System zu implementieren, aber warum, wenn jemand anderes es bereits getan hat?) – jrockway

+0

Ich dachte, dies könnte mit Methodenattributen gemacht werden, aber jetzt kann ich die entsprechende perldoc-Seite nicht finden ist nicht die Seite, an die ich gedacht habe). Ich erinnere mich an ein schönes Beispiel mit verschiedenen Methoden zum Hinzufügen in Protokollierung ... – Ether

+0

Siehe diese Frage SO für weitere Informationen: http://StackOverflow.com/Questions/635212/how-i-redefine-perl-Class-Methods – draegtun

Antwort

14

Ja, Sie können Perl-Unterprogrammaufrufe abfangen. Ich habe ein ganzes Kapitel über diese Art von Dingen in Mastering Perl. Schauen Sie sich das Modul Hook::LexWrap an, mit dem Sie das tun können, ohne alle Details durchgehen zu müssen. Perls Methoden sind nur Subroutinen.

Sie können auch eine Unterklasse erstellen und die Methode überschreiben, die Sie abfangen möchten. Das ist ein etwas besserer Weg, weil das objektorientierte Programmieren so machen will. Manchmal schreiben jedoch Leute Code, der es Ihnen nicht erlaubt, dies richtig zu machen. Es gibt mehr darüber in Mastering Perl auch.

6

Das sieht nach einem Job für Moose aus! Moose ist ein Objektsystem für Perl, das das und noch viel mehr kann. Die docs wird viel besser erklären, als ich kann, aber was Sie wahrscheinlich wollen, ist ein Method Modifier, speziell before.

+1

Elch ist etwas, das Sie für die gesamte Anwendung verwenden, nicht gezielte Probleme. –

+3

Aber es hängt davon ab, was das Problem ist, und der Fragesteller der Frage gibt keine Details an. Es könnte jemand sein, der einen Ansatz auswertet, den man bei der Erstellung einer neuen Anwendung anstellen sollte, und in diesem Fall wäre es eine angemessene und nützliche Antwort, wenn man sich an Moose wendet. – jsoverson

+1

Wir sagten nur das Gleiche. Ich habe nicht gesagt, ich solle Elch nicht gebrauchen, aber ich habe auch nicht gesagt, es zu benutzen. –

7

Um kurz zu beschreiben, Perl hat die Fähigkeit, Symboltabelle zu ändern. Sie rufen ein Unterprogramm (Methode) über die Symboltabelle des Pakets auf, zu dem die Methode gehört. Wenn Sie die Symboltabelle ändern (und dies wird nicht als sehr schmutzig betrachtet), können Sie die meisten Methodenaufrufe durch Aufrufen der anderen von Ihnen angegebenen Methoden ersetzen. Dies zeigt die Vorgehensweise:

# The subroutine we'll interrupt calls to 
sub call_me 
{ 
    print shift,"\n"; 
} 

# Intercepting factory 
sub aspectate 
{ 
    my $callee = shift; 
    my $value = shift; 
    return sub { $callee->($value + shift); }; 
} 
my $aspectated_call_me = aspectate \&call_me, 100; 

# Rewrite symbol table of main package (lasts to the end of the block). 
# Replace "main" with the name of the package (class) you're intercepting 
local *main::call_me = $aspectated_call_me; 

# Voila! Prints 105! 
call_me(5); 

Dies zeigt auch, dass, wenn schon jemand anhand des Unterprogramms nimmt und ruft sie über den Bezug, können Sie nicht mehr solche Anrufe beeinflussen.

Ich bin mir ziemlich sicher, dass es Frameworks gibt, die Aspiration in Perl durchführen, aber das, hoffe ich, zeigt den Ansatz.

3

Ja.

Sie brauchen drei Dinge:

Die Argumente für ein Gespräch in @_, die nur ein weiteres dynamisch scoped Variable ist.

Dann unterstützt goto ein Reference-Sub-Argument, das die aktuelle @_ erhält, aber einen anderen (Tail-) Funktionsaufruf macht.

Schließlich kann local verwendet werden, um lexikalisch begrenzte globale Variablen zu erstellen, und die Symboltabellen sind in %:: eingebettet.

So haben Sie bekam:

sub foo { 
    my($x,$y)=(@_); 
    print "$x/$y = " . ((0.0+$x)/$y)."\n"; 
} 
sub doit { 
    foo(3,4); 
} 
doit(); 

die natürlich druckt:

3/4 = 0.75 

Wir foo local verwenden ersetzen können und gehen:

my $oldfoo = \&foo; 
local *foo = sub { (@_)=($_[1], $_[0]); goto $oldfoo; }; 
doit(); 

Und jetzt bekommen wir :

4/3 = 1.33333333333333 

Wenn Sie wollen *foo ändern, ohne seinen Namen zu verwenden, und Sie wollten nicht eval verwenden, dann könnte man es ändern, indem %::, zum Beispiel der Manipulation:

$::{"foo"} = sub { (@_)=($_[0], 1); goto $oldfoo; }; 
doit(); 

Und wir bekommen jetzt:

3/1 = 3 
5

Sie können, und Pavel beschreibt eine gute Möglichkeit, es zu tun, aber Sie sollten wahrscheinlich erarbeiten, warum Sie dies in erster Linie tun möchten.

Wenn Sie nach erweiterten Möglichkeiten suchen, Aufrufe an beliebige Subroutinen abzufangen, dann funktioniert das Hantieren mit Symboltabellen für Sie, aber wenn Sie Funktionalität zu Funktionen hinzufügen möchten, die vielleicht in den Namensraum exportiert werden, in dem Sie gerade arbeiten, Dann müssen Sie möglicherweise wissen, wie Sie Funktionen aufrufen können, die in anderen Namespaces vorhanden sind.

Zum Beispiel exportiert Data :: Dumper normalerweise die Funktion 'Dumper' in den aufrufenden Namespace, aber Sie können diese überschreiben oder deaktivieren und eine eigene Dumper-Funktion bereitstellen, die dann das Original über den vollständig qualifizierten Namen aufruft.

z.B.

Wieder ist dies eine alternative Lösung, die je nach dem ursprünglichen Problem besser geeignet sein kann. Es kann viel Spaß gemacht werden, wenn man mit der Symboltabelle spielt, aber es kann übertrieben sein und dazu führen, dass Code schwer zu pflegen ist, wenn man ihn nicht braucht.

+1

In diesem Fall sollten Sie eine leere Importliste zu Dumper geben, damit Sie nichts importieren. In diesem Fall spielt auch die Reihenfolge eine Rolle. Sie müssen zuerst importieren und dann überschreiben. Die letzte Unterprogrammdefinition gewinnt. Schließlich erhalten Sie unter Warnungen einen "Neudefinieren" -Fehler. –

+0

Auf jeden Fall nur versuchen, es einfach zu halten, um ein Beispiel zu nennen. Direkt zu sein hat mehr Wert (und ist für den Antwortenden schneller) als jedes tangentiale Detail zu erklären. Die Konzepte des Imports und der Warnungen würden am ehesten als andere Fragen erklärt. – jsoverson

Verwandte Themen