Wenn Sie eine Methode mit Affen patchen, können Sie die überschriebene Methode von der neuen Implementierung aufrufen?
Sagen, ich bin monkey-patching eine Methode in einer Klasse, wie könnte ich nennen, die überschriebene Methode aus der übergeordneten Methode? I. e. Etwas, das ein bisschen wie super
E. g.
class Foo
def bar()
"Hello"
end
end
class Foo
def bar()
super() + " World"
end
end
>> Foo.new.bar == "Hello World"
InformationsquelleAutor der Frage James Hollingworth | 2010-12-17
Du musst angemeldet sein, um einen Kommentar abzugeben.
BEARBEITEN: Es hat schon 5 Jahre her, seit ich ursprünglich schrieb diese Antwort, und er verdient einige kosmetische Chirurgie zu halten, aktuell.
Können Sie sehen die Letzte version vor dem edit -hier.
Können Sie nicht, rufen Sie die überschrieben Methode von Namen oder Schlüsselwörtern. Das ist einer der vielen Gründe, warum monkey-patching sollten vermieden werden und der Vererbung bevorzugt werden, statt, da Sie natürlich kann rufen Sie die überschrieben Methode.
Vermeidung Von Monkey-Patching
Vererbung
Also, wenn überhaupt möglich, sollten Sie lieber so etwas wie dieses:
Dies funktioniert, wenn Sie die Kontrolle Erstellung der
Foo
Objekte. Ändern Sie einfach jeden Ort, der schafft eineFoo
zu erstellen Sie stattdessen eineExtendedFoo
. Das funktioniert noch besser, wenn Sie die Dependency Injection Design Patterndie Factory Method Design Patterndie Abstract Factory Design Pattern oder etwas entlang jenen Linien, da in diesem Fall, es ist der einzige Ort, den Sie brauchen, um zu ändern.Delegation
Wenn Sie nicht Erstellung der
Foo
Objekte, zum Beispiel, weil Sie geschaffen sind, durch einen Rahmen, der außerhalb Ihrer Kontrolle (wie ruby-on-rails zum Beispiel), dann könnten Sie den Wrapper Design Pattern:Grundsätzlich an der Grenze des Systems, wo die
Foo
Objekt kommt in den code, wickeln Sie es in ein anderes Objekt, und verwenden Sie dann dass Objekt statt der ursprünglichen überall in Ihrem code.Diese nutzt die
- Objekt#DelegateClass
Helfer-Methode aus derdelegieren
Bibliothek in der stdlib."Sauber" Monkey-Patching
Module#prepend
: Mixin VoranstellenDen beiden oben genannten Methoden erfordern eine änderung des Systems zu vermeiden, monkey-patching. Dieser Abschnitt zeigt die bevorzugte und am wenigsten invasive Methode der monkey-patching, sollte das system wechseln und nicht eine option sein.
Module#prepend
wurde Hinzugefügt, um Unterstützung mehr oder weniger genau diesen Anwendungsfall.Module#prepend
macht das gleiche wieModule#include
außer es mischt sich in die mixin direkt unten der Klasse:Hinweis: ich schrieb auch ein wenig über
Module#prepend
in dieser Frage: Ruby-Modul voranstellen vs AbleitungMixin-Vererbung (kaputt)
Ich habe gesehen, einige Leute versuchen (und Fragen Sie, warum es hier nicht funktioniert auf StackOverflow) so etwas wie dieses, D. H.
include
ing ein mixin stattprepend
ing:Leider, das wird nicht funktionieren. Es ist eine gute Idee, denn es nutzt Vererbung, was bedeutet, dass Sie verwenden können
super
. Allerdings- Modul#include
fügt Mixins oben die Klasse in der Vererbungshierarchie, was bedeutet, dassFooExtensions#bar
wird nie aufgerufen werden (und wenn es wurden genannt, diesuper
würde nicht wirklich findenFoo#bar
sondernObject#bar
welche nicht vorhanden ist), daFoo#bar
wird immer gefunden werden, ersten.Methode Verpackung
Die große Frage ist: wie können wir halten, um die
bar
Methode, ohne tatsächlich halten, um eine eigentliche Methode? Die Antwort liegt, wie so oft, in die funktionale Programmierung. Wir fassen die Methode als eine tatsächliche Objektund wir verwenden einen Verschluss (z.B. ein block), um sicherzustellen, dass wir und nur wir halten, um das Objekt:Dieser ist sehr sauber: da
old_bar
ist nur eine lokale variable geht out of scope am Ende der Klasse Körper, und es ist unmöglich, es von überall aus zugreifen, auch mit der spiegelung! Und daModul#define_method
nimmt einen block, und Blöcke in der Nähe über die Sie umgebenden lexikalischen Umgebung (das ist warum wir sind mitdefine_method
stattdef
hier), es (und nur es) wird immer noch Zugriff aufold_bar
auch nachdem er gegangen ist out of scope.Kurze Erklärung:
Hier sind wir die Verpackung des
bar
Methode in eineUnboundMethod
Methode-Objekts und Zuweisung an die lokale variableold_bar
. Das heißt, wir haben jetzt einen Weg zu halten, umbar
auch wenn es überschrieben wurde.Dies ist ein bisschen schwierig. Im Grunde, in Ruby (und in so ziemlich alle single-dispatch-basierte OO-Sprachen), eine Methode, die gebunden ist an einen bestimmten Empfänger-Objekt, genannt
self
in Ruby. In anderen Worten: eine Methode, die immer weiß, von welchem Objekt es aufgerufen wurde, es weiß, was seineself
ist. Aber, wir schnappten uns die Methode, direkt aus einer Klasse, wie weiß er, was seineself
ist?Gut, tut es nicht, das ist, warum wir brauchen, um
bind
unsereUnboundMethod
auf ein Objekt zunächst, die Rückkehr einerMethode
Objekt, das wir dann aufrufen kann. (UnboundMethod
s kann nicht aufgerufen werden, weil Sie nicht wissen, was zu tun ist, ohne zu wissen, Ihreself
.)Und was tun wir
bind
? Wir haben einfachbind
es uns selbst, so wird es sich Verhalten genau wie das originalbar
hätte!Schließlich müssen wir die zu nennen, die
Method
zurückgegeben wird, die vonbind
. In Ruby 1.9, es gibt einige nette neue syntax (.()
), aber wenn Sie sind auf 1,8, können Sie einfach diecall
Methode; das ist es, was.()
übersetzt wird, sowieso.Hier sind ein paar andere Fragen, wo einige dieser Begriffe werden erläutert:
"Dirty" Monkey-Patching
alias_method
KetteDas problem, das wir haben mit unserem monkey-patching ist, dass, wenn wir überschreiben die Methode, die Methode ist verschwunden, so dass wir es nicht nennen mehr. Also, lasst uns einfach eine backup-Kopie!
Das problem mit diesem ist, dass wir jetzt haben verunreinigt den namespace mit einem überflüssigen
old_bar
Methode. Diese Methode wird in unserer Dokumentation haben, wird es zeigen sich in der code-Vervollständigung in unserem IDEs, wird es zeigen sich während der Reflexion. Auch, es kann immer noch genannt werden, aber vermutlich werden wir monkey gepatcht, weil wir nicht wie sein Verhalten in den ersten Platz, so dass wir vielleicht nicht wollen, dass andere Menschen es nennen.Trotz der Tatsache, dass dies hat einige unerwünschte Eigenschaften, es hat leider popularisiert durch AciveSupport ist
Modul#alias_method_chain
.Nebenbei: Verfeinerungen
Im Fall brauchen Sie nur das unterschiedliche Verhalten in ein paar bestimmten stellen und nicht im gesamten system, die Sie verwenden können, Verfeinerungen zu beschränken, die monkey patch auf einen bestimmten Gültigkeitsbereich. Ich werde zeigen, dass er hier mit der
Module#prepend
Beispiel von oben:Können Sie sehen, eine anspruchsvolle Beispiel für die Verwendung von Verfeinerungen in dieser Frage: Aktivieren der monkey-patch für bestimmte Methode?
Aufgegeben Ideen
Bevor die Ruby-community ließ sich auf
Module#prepend
gab es mehrere verschiedene Ideen im Umlauf, die Sie gelegentlich sehen, auf die in älteren Diskussionen. Alle diese sind zusammengefasst durchModule#prepend
.Methode Combinators
Einer Idee wurde die Idee der Methode combinators von CLOS. Dies ist im Grunde eine sehr einfache version einer Teilmenge der Aspekt-Orientierten Programmierung.
Verwenden Sie eine syntax wie die
würden Sie in der Lage sein zu "hook" die Ausführung der
bar
Methode.Es ist allerdings nicht ganz klar, ob und wie bekommen Sie Zugang zu
bar
's return-Wert innerhalbbar:after
. Vielleicht könnten wir (ab -) Nutzung dersuper
keyword?Ersatz
Die vor combinator ist äquivalent zu
prepend
ing ein mixin mit einer übergeordneten Methode aufruftsuper
ganz Ende der Methode. Ebenso die nach dem combinator ist äquivalent zuprepend
ing ein mixin mit einer übergeordneten Methode aufruftsuper
ganz Anfang der Methode.Können Sie auch Dinge zu tun, bevor und nach dem Aufruf
super
sind, können Sie anrufensuper
mehrere Male, und sowohl das abrufen und Bearbeiten vonsuper
's return-Wert, so dassprepend
mächtiger als Methode combinators.und
old
StichwortDiese Idee fügt ein neues Stichwort-ähnlich
super
die können Sie aufrufen, die überschrieben Methode die gleiche Weisesuper
können Sie rufen Sie die überschrieben Methode:Das größte problem mit diesem ist, dass es rückwärts ist nicht kompatibel: wenn Sie Methode namens
old
Sie nicht mehr in der Lage sein, es zu nennen!Ersatz
super
in eine überschreibende Methode in einerprepend
ed mixin ist im wesentlichen der gleiche wieold
in diesem Vorschlag.redef
StichwortÄhnlich wie oben, aber anstatt das hinzufügen eines neuen Schlüsselwort für aufrufen die überschriebene Methode und verlassen
def
allein, wir fügen Sie ein neues Schlüsselwort für Neudefinition Methoden. Das ist abwärtskompatibel, da die syntax ist derzeit sowieso illegal:Anstatt zwei neue keywords, könnten wir auch neu definieren, die Bedeutung von
super
innenredef
:Ersatz
redef
ining eine Methode ist äquivalent zum überschreiben der Methode in einemprepend
ed mixin.super
in der übergeordneten Methode verhält sich wie diesuper
oderold
in diesem Vorschlag.InformationsquelleAutor der Antwort Jörg W Mittag
Werfen Sie einen Blick auf aliasing-Verfahren, ist diese Art der Umbenennung der Methode unter einem neuen Namen.
Weitere Informationen und ein Ausgangspunkt, nehmen Sie einen Blick auf diese Austausch-Methoden-Artikel (vor allem der erste Teil).
Die Ruby-API-docs, bietet auch die (weniger aufwändige) Beispiel.
InformationsquelleAutor der Antwort Veger
Die Klasse, die machen überschreiben muss neu geladen werden nach der Klasse, die enthält die ursprüngliche Methode, so
require
es in der Datei, wird overrride.InformationsquelleAutor der Antwort rplaurindo