10 Stimmen

Alternativen zum Affen Parcheando Kernklassen

Ich bin noch neu in Ruby und schreibe im Grunde gerade mein erstes Mikroprogramm, nachdem ich Coopers Buch beendet habe. Ich wurde darauf hingewiesen, den Affen Parcheando zu vermeiden, aber das Problem ist, dass ich nicht weiß, was die Alternativen sind, um das gleiche Verhalten zu erreichen. Im Grunde möchte ich eine neue Methode hinzufügen, auf die jedes String-Objekt zugreifen kann. Der offensichtliche monkey-Parcheando Weg ist zu:

class String
  def do_magic
    ...magic...
  end
end

Ich erinnere mich, dass es einen Weg mit String.send gibt. Aber ich kann mich weder daran erinnern, wie es gemacht wird, noch wo ich es gelesen habe. Kann jemand darauf hinweisen, alle Alternativen, die noch lassen Sie mich diese Methode für die String-Klasse und Kind-Objekte zur Verfügung stellen würde?

15voto

Chuck Punkte 228137

Jede andere Möglichkeit, dies zu tun, wäre nur eine umständlichere Syntax für monkey Parcheando. Es gibt Möglichkeiten, die send et eval und alles Mögliche, aber warum ? Machen Sie es auf die offensichtliche Weise.

Sie sollten vorsichtig sein mit Parcheando in großen Projekten oder wenn Sie Abhängigkeiten haben, denn es kann zu Konflikten kommen, wenn mehrere Hände an der gleichen Stelle herumspielen. Das bedeutet nicht, dass man nach einer alternativen Syntax suchen soll, die das Gleiche erreicht - es bedeutet, dass man vorsichtig sein soll, wenn man Änderungen vornimmt, die sich auf Code auswirken könnten, der nicht der eigene ist. In Ihrem speziellen Fall ist das wahrscheinlich kein Problem. Es ist nur etwas, das bei größeren Projekten beachtet werden muss.

Eine Alternative in Ruby ist, dass man einem einzelnen Objekt Methoden hinzufügen kann.

a = "Hello"
b = "Goodbye"
class <<a
  def to_slang
    "yo"
  end
end
a.to_slang # => "yo"
b.to_slang # NoMethodError: undefined method `to_slang' for "Goodbye":String

6voto

Aaron Hinni Punkte 14348

Wenn Sie eine neue Methode hinzufügen möchten, auf die jedes String-Objekt zugreifen kann, dann sollten Sie es so machen, wie Sie es gemacht haben.

Eine gute Praxis ist es, Ihre Erweiterungen zu Kernobjekten in einer separaten Datei zu speichern (wie string_ex.rb ) oder ein Unterverzeichnis (wie extensions o core_ext ). Auf diese Weise ist es offensichtlich, was erweitert wurde, und es ist für jemanden leicht zu sehen, wie sie erweitert oder geändert wurden.

Wo Affe Parcheando schiefgehen kann, ist, wenn Sie ein bestehendes Verhalten eines Kernobjekts ändern, das dazu führt, dass ein anderer Code, der die ursprüngliche Funktionalität erwartet, sich falsch verhält.

2voto

dylanfm Punkte 6220

Le site object Klasse definiert send und alle Objekte erben dies. Sie "senden" einem Objekt die send Methode. Die Website send Die Parameter der Methode sind der Name der Methode, die Sie aufrufen wollen, als Symbol, gefolgt von allen Argumenten und einem optionalen Block. Sie können auch __send__ .

>> "heya".send :reverse
=> "ayeh"

>> space = %w( moon star sun galaxy )
>> space.send(:collect) { |el| el.send :upcase! }
=> ["MOON", "STAR", "SUN", "GALAXY"]

Bearbeiten.

Wahrscheinlich möchten Sie die define_method Methode:

String.class_eval {
  define_method :hello do |name|
    self.gsub(/(\w+)/,'hello') + " #{name}"
  end
}

puts "Once upon a time".hello("Dylan")
# >> hello hello hello hello Dylan

Das fügt Instanzmethoden hinzu. Um Klassenmethoden hinzuzufügen:

eigenclass = class << String; self; end
eigenclass.class_eval {
  define_method :hello do
    "hello"
  end
}

puts String.hello
# >> hello

Sie können jedoch keine Methoden definieren, die einen Block erwarten.

Es könnte eine gute Sache sein, Folgendes zu lesen dieses Kapitel aus Why's Poignant Guide können Sie zu "Dwemthy's Array" springen, um zur Metaprogrammierung zu gelangen.

1voto

dmondark Punkte 1649

Danke, Leute.

Alle vorgeschlagenen Umsetzungen funktionieren. Noch wichtiger ist, dass ich gelernt habe, im konkreten Fall abzuwägen und zu entscheiden, ob die Wiedereröffnung von Kernklassen (oder Bibliotheksklassen) eine gute Idee ist oder nicht.

FWIW, ein Freund wies auf die send Umsetzung, die ich gesucht habe. Aber jetzt, wo ich es betrachte, ist es sogar näher an Monkeypatching als alle anderen Implementierungen :)

module M
    def do_magic
    ....
    end
end
String.send(:include, M)

1voto

rausch Punkte 2878

Als Alternative zum Anhängen von Funktionen an Klassen oder Objekte können Sie auch den funktionalen Weg gehen:

class StringMagic
  def self.do(string)
     ...
  end
end

StringMagic.do("I'm a String.") # => "I'm a MAGIC String!"

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X