Angenommen, ich habe eine Klasse:
%Vor%Um dieser Klasse eine Methode hinzuzufügen, kenne ich 2 Optionen:
Erneutes Öffnen der Klasse und Implementieren der Methode:
%Vor% mit class_eval
zur Implementierung der Methode:
Was ist der Unterschied? Welcher ist besser?
Tatsächlich gibt es ein paar andere Möglichkeiten, einer Klasse neue Methoden hinzuzufügen. Sie können beispielsweise auch die Methoden in einem Modul definieren und das Modul in die ursprüngliche Klasse einfügen.
%Vor%Es gibt kein wirklich besseres oder schlechteres. Die zwei (oder drei) Wege, die Sie erwähnten, haben ein unterschiedliches Verhalten und Sie möchten vielleicht das eine oder andere verwenden, je nach Ihrem Bedürfnis (oder Ihrer Präferenz). In den meisten Fällen ist dies subjektiv. In anderen Fällen hängt es wirklich davon ab, wie Ihr Code strukturiert ist.
Der Hauptunterschied zwischen dem erneuten Öffnen der Klasse und der Verwendung von class_eval
besteht darin, dass die erste Klasse ebenfalls eine Klassendefinition ist, während die zweite die ursprüngliche Klasse bereits definiert.
In der Praxis kann das erneute Öffnen der Klasse in einigen Fällen zu unerwarteten Nebenwirkungen führen. Nehmen wir an, Sie haben Foo
in der Datei lib/foo.rb
mit einer Reihe von Methoden definiert. Dann öffnest du wieder Foo
in config/initializers/extra.rb
und fügst die bar
Methode hinzu.
In myclass.rb
verwenden Sie Foo
, aber anstatt lib/foo.rb
manuell zu verwenden, verlassen Sie sich auf eine automatische Ladefunktion.
Wenn extra.rb
vor lib/foo.rb
geladen wird, könnte die Foo
-Klasse in Ihrer Umgebung bereits definiert sein, und Ihr Code lädt lib/foo.rb
nicht. Was Sie haben werden, ist eine Klasse Foo
, die nur die von Ihnen definierte Erweiterung bar
und nicht die ursprüngliche Foo
eins enthält.
Mit anderen Worten, wenn Sie aus irgendeinem Grund die Klasse erneut öffnen, um einige Methoden hinzuzufügen, ohne sicherzustellen, dass die vollständige Originalklassendefinition zuerst (oder danach) geladen wird, kann Ihr Code brechen, wenn er auf autoload basiert.
Umgekehrt ruft Foo.class_eval
eine Methode für Foo
auf, daher erwartet es, dass die ursprüngliche Foo
-Definition bereits zu dem Zeitpunkt vorhanden ist, an dem Sie versuchen, neue Methoden hinzuzufügen. Dadurch wird sichergestellt, dass beim Hinzufügen neuer Methoden die Klasse Foo
bereits definiert ist.
Zusammenfassend besteht der Hauptunterschied darin, dass das Wiedereröffnen der Klasse es Ihnen erlaubt (im Guten oder im Schlechten), Methoden zu einer Klasse hinzuzufügen, die noch nicht geladen wurde, während class_eval
erfordert, dass die Klasse bereits definiert ist.
Im Allgemeinen bevorzuge ich den zweiten Ansatz, es sei denn, ich definiere Namespace-Unterklassen oder wiederöffnende Klassen, in denen ich die volle Kontrolle habe, da der Code in großen Codebasen mehr wartbar bleibt. Tatsächlich verwende ich normalerweise Mixins, wenn ich Klassen von Drittanbietern erweitere, so dass ich die gesamte Vorfahrenkette der Methode behalten kann, wenn ich bestehende Methoden überschreiben muss.