猴子修补ruby中的类的推荐方法

sta*_*ona 27 ruby monkeypatching

我注意到有两种常见的方法来修补ruby中的类:

在类上定义新成员,如下所示:

class Array
   def new_method
     #do stuff
   end
end
Run Code Online (Sandbox Code Playgroud)

并在类对象上调用class_eval:

Array.class_eval do
   def new_method
      #do stuff
   end
end
Run Code Online (Sandbox Code Playgroud)

我想知道两者之间是否存在任何差异,使用一种方法是否优于另一种方法?

KL-*_*L-7 57

老实说,我曾经使用过第一种形式(重新开课),因为感觉更自然,但是你的问题迫使我对这个主题做了一些研究,这就是结果.

重新打开该类的问题在于,如果由于某种原因您打算重新打开的原始类目前尚未定义,它将默默地定义一个新类.结果可能会有所不同:

  1. 如果你没有覆盖任何方法但只添加新的方法并且定义了原始实现(例如,文件,最初定义了类的位置),以后一切都会好的.

  2. 如果重新定义某些方法并且稍后加载原始方法,则将使用其原始版本覆盖您的方法.

  3. 最有趣的情况是当您使用标准自动加载或某些花哨的重载机制(如Rails中使用的那种)来加载/重新加载类.其中一些解决方案依赖于引用未定义常量时调用的const_missing.在这种情况下,自动加载机制会尝试查找未定义的类的定义并加载它.但是,如果您自己定义类(当您打算重新打开已定义的类)时,它将不再"丢失",原始可能永远不会被加载,因为不会触发自动加载机制.

另一方面,如果您使用,如果class_eval目前没有定义课程,您将立即得到通知.此外,当您在调用其class_eval方法时引用该类时,任何自动加载机制都将有机会定位类的定义并加载它.

考虑到这一点class_eval似乎是一种更好的方法.虽然,我很乐意听到其他一些意见.


And*_*iep 9

范围

我认为,KL-7没有指出的一个重大区别是您的新代码将被解释的范围:

如果您(重新)打开一个类来操作它,您添加的新代码将在(原始)类的范围内进行解释.
如果您使用的模块#class_eval操纵类,添加新的代码将在周边您的来电#class_eval范围进行解释,并不会意识到类范围.如果一个人不知道,这种行为可能违反直觉并导致难以调试的错误.

CONSTANT    = 'surrounding scope'

# original class definition (uses class scope)
class C
  CONSTANT  = 'class scope'

  def fun()  p CONSTANT  end
end
C.new.fun    # prints: "class scope"


# monkey-patching with #class_eval: uses surrounding scope!
C.class_eval do
  def fun()  p CONSTANT  end
end
C.new.fun    # prints: "surrounding scope"


# monkey-patching by re-opening the class: uses scope of class C
class C
  def fun()  p CONSTANT  end
end
C.new.fun    # prints: "class scope"
Run Code Online (Sandbox Code Playgroud)