为什么 Ruby 改进只修改 类,而不修改模块?

Why do Ruby refinements only modify classes, not modules?

Ruby docs on refinements state:

Refinements only modify classes, not modules so the argument must be a class.

这是为什么?

可以对模块进行猴子修补:

module MyModule
  def my_method
    "hello"
  end
end

include MyModule
puts my_method # => hello

module MyModule
  def my_method
    "goodbye"
  end
end

puts my_method # => goodbye

我敢肯定这不是个好主意,但如果您能限制这种猴子补丁的范围,它可能不会那么糟糕。那你为什么不能?

Ruby 中的 refine 旨在处理猴子修补和继承问题,其目的是将猴子修补限制在 classes 实例中特定命名空间。

这些相同的继承问题不会以相同的方式应用于模块,因为模块可以使用 mixin 扩展或包含在其他模块中(与 classes 相反)。

这将允许通过创建一个新模块来限制猴子补丁的命名空间,该新模块在其自己的命名空间内扩展并覆盖原始模块。

如果使用你的例子:

module MyModule
  def my_method
    "hello"
  end
end

include MyModule

puts my_method
# => hello

module MyOtherModule
  extend MyModule

  puts my_method # will print: hello

  def my_method
    "goodbye"
  end
  extend self

  puts my_method # will print: goodbye
end
# => hello
# => goodbye

puts my_method
# => hello

如您所见,我们设法将 'monkey-patch' 限制在 MyOtherModule 命名空间而不使用 refine

因为我们没有使用 MyModule 的实例(MyModule 不是 class),这种方法非常有效。

classes 不可能做到这一点,因为除其他原因外,class 实例可能不限于使用它们的模块的命名空间...因此,对于Class 个实例,应使用 refine 个。

模块的改进与 类 的改进一样有用。考虑如何

module Foobar
  refine Enumerable
    def all_zero?; all? &:zero? end
  end
end

比直接猴子修补更有礼貌:

module Enumerable
  def all_zero?; all? &:zero? end
end

有一个implementation issue with refining modules, which seems to be solved by now