9

我对 Ruby 还是很陌生,基本上只是在完成 Cooper 的书之后才编写我的第一个微程序。我被指出了避免猴子修补的方向,但问题是我不知道实现相同行为的替代方法是什么。基本上,我想添加一个每个字符串对象都可以访问的新方法。显而易见的猴子修补方法是:

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

我记得有一种使用 String.send 的方法。但我不记得它是如何完成的,也不记得我在哪里读到的。谁能指出仍然让我将该方法用于 String 类和子对象的任何替代方法?

4

6 回答 6

15

这样做的任何其他方式都只是猴子修补的一种更尴尬的语法。有涉及各种事物的方法,但是send为什么?继续并以明显的方式进行。eval

在大型项目中或当你有依赖项时,你要小心猴子修补,因为当几只手都在同一个地方搞砸时,你可能会发生冲突。这并不意味着要寻找一种替代语法来完成相同的事情——这意味着当您进行可能影响不属于您的代码的更改时要小心。在您的特定情况下,这可能不是问题。这只是大型项目中可能需要解决的问题。

Ruby 中的一种替代方法是您可以向单个对象添加方法。

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
于 2009-03-29T22:28:48.180 回答
6

如果您想添加一个可供每个字符串对象访问的新方法,那么按照您拥有的方式进行操作就是如何完成它。

一个好的做法是将核心对象的扩展放在单独的文件(如string_ex.rb)或子目录(如extensionscore_ext)中。这样一来,扩展的内容就很明显了,人们很容易看到它们是如何扩展或更改的。

猴子修补可能会出错的地方是,当您更改核心对象的某些现有行为时,会导致其他一些期望原始功能行为不端的代码。

于 2009-03-30T05:04:30.413 回答
2

该类object定义了send,所有对象都继承了它。你“发送”一个对象的send方法。该send方法的参数是您想要调用的方法的名称作为符号,后跟任何参数和一个可选块。您也可以使用__send__.

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

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

编辑..

您可能想使用以下define_method方法:

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

这增加了实例方法。添加类方法:

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

puts String.hello
# >> hello

但是,您不能定义需要块的方法。

阅读Why's Poignant Guide中的这一章可能是件好事,您可以跳到“Dwemthy 的数组”以了解元编程的内容。

于 2009-03-29T22:56:09.207 回答
1

多谢你们。

所有建议的实施工作。更重要的是,我学会了权衡手头的案例,并决定重新打开核心(或库)类是否是一个好主意。

FWIW,一位朋友指出了send我正在寻找的实现。但是现在我看它,它比所有其他实现更接近于猴子补丁:)

module M
    def do_magic
    ....
    end
end
String.send(:include, M)
于 2009-03-30T18:49:34.610 回答
1

作为将函数附加到类或对象的替代方法,您始终可以采用函数式路线:

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

StringMagic.do("I'm a String.") # => "I'm a MAGIC String!"
于 2013-11-07T09:23:32.867 回答
0

如果其他人想要您的代码(例如,作为 gem),您描述的“猴子补丁”确实可能是一个问题。谁说他们不想添加一个名为 do_magic 的 String 方法?一种方法将覆盖另一种方法,这可能很难调试。如果您的代码有可能是开源的,那么最好创建自己的类:

class MyString < String
  def initialize(str)
    @str = str
  end
  def do_magic
    ...magic done on @str
    @str
  end
end

现在,如果你需要 do_magic 你可以

magic_str = MyString.new(str).do_magic
于 2016-04-06T22:18:08.087 回答