Ruby的“修补”究竟意味着什么?

根据维基百科, 猴子补丁是:

在不改变原始源代码的情况下扩展或修改dynamic语言的运行时代码的一种方法。

以下来自同一条目的陈述使我困惑不解:

在Ruby中,“猴子补丁”一词被误解为对类的任何dynamic修改,常被用作在运行时dynamic修改任何类的同义词。

我想知道Ruby中的猴子修补的确切含义。 它做的是像下面这样的东西,还是其他的东西?

class String def foo "foo" end end 

简单的答案是没有“确切”的含义,因为这是一个新词,不同的人使用它的方式不同。 这至less可以从维基百科的文章中看出来。 有些人坚持认为它只适用于“运行时”代码(我猜想是内置类),而有些人会用它来指代任何类的运行时修改。

就个人而言,我更喜欢更具包容性的定义。 毕竟,如果我们只使用修改内置类的术语,那么我们如何引用所有其他类的运行时修改呢? 对我来说重要的是源代码和实际运行的类有区别。

在Ruby中,“猴子补丁”一词被误解为对类的任何dynamic修改,常被用作在运行时dynamic修改任何类的同义词。

上面的声明断言,Ruby的使用是不正确的 – 但是术语的演变,并不总是一件坏事。

我听说猴子打补丁/鸭子打的最好的解释是Patrick Ewing在RailsConf 2007中

如果它像鸭子一样走路,像鸭子一样说话,那是鸭子,对吧? 所以如果这只鸭子没有给你想要的噪音,那么你只需要打鸭子,直到它返回你所期望的。

猴子修补是在运行时replace类的方法(不像其他人所描述的那样添加新方法)。

除了是一个非常不明显和难以debugging的方式来改变代码,它不会缩放; 随着越来越多的模块启动猴子修补方法,相互变化的可能性越来越大。

你是对的; 这是当你修改或扩展现有的类而不是子类。

这是猴子补丁:

 class Float def self.times(&block) self.to_i.times { |i| yield(i) } remainder = self - self.to_i yield(remainder) if remainder > 0.0 end end 

现在我想这有时可能会有用,但想象一下,如果你看到例行公事。

 def my_method(my_special_number) sum = 0 my_special_number.times { |num| sum << some_val ** num } sum end 

当它被调用时,它只是偶尔打破。 对于那些注意到你已经知道为什么,但想象你不知道有一个.times类方法的浮点types,你自动假定my_special_number是一个整数。 每次参数是一个整数,整数或浮点数,它都可以正常工作(除了存在浮点余数时,整个整数将被传回)。 但是,通过一个数字与小数点中的任何东西,它肯定会打破!

试想一下,您的gem,Rails插件甚至是您自己的项目中的同事可能会发生这种情况。 如果有这样一个或两个小方法,可能需要一些时间才能find和纠正。

如果你想知道为什么它会中断,请注意sum是一个整数,浮点数可以返回; 另外,指数符号只在types相同时才起作用。 所以你可能会认为它是固定的,因为你把麻烦的数字转换成浮点数……只是发现总和不能得到浮点结果。

Ruby最强大的方面之一是能够重新打开任何类并改变它的方法。

是的,你可以重新开课,改变它的工作方式。 这包括标准的Ruby类

 String, Array or Hash! 

现在这听起来很危险。 能够改变方法的预期结果可能会导致各种奇怪的行为,难以追踪错误。

但是,“Monkey Patch”任何课程的能力是非常强大的。 ruby就像一把锋利的刀子,它可以是非常有效的,但是如果你削减自己,这通常是你自己的错。

首先,我们将添加一个方便的方法来生成一些Lorem Ipsum文本:

 class String def self.lipsum "Lorem ipsum dolor sit amet, consectetur adipiscing elit." end end 

在这个例子中,我已经重新打开了String核心类,并添加了一个双眼法。

 String.lipsum => "Lorem ipsum dolor sit amet, consectetur adipiscing elit." 

但是,我们不仅可以将方法添加到核心的String类中,还可以修改现有方法的行为!

 class String def upcase self.reverse end end 

在这个例子中,我们劫持了upcase方法,而是调用reverse方法!

 "hello".upcase => "olleh" 

正如你所看到的,即使你不拥有这个类,或者它是Ruby核心的一部分,在现有的类上添加或修改方法也是非常容易的。

什么时候应该使用猴子补丁?

很less。

Ruby为我们提供了大量function强大的工具。 但是,仅仅因为一个工具是强大的,并不能成为工作的正确工具。

猴子补丁特别是一个非常强大的工具。 然而,错误的手中一个强大的工具会造成无尽的痛苦和痛苦。

每当你猴子补上一个class,你可能会在未来的某个时刻出现头痛的时候出现问题。

Monkey Patched的类更难以理解和debugging。 如果你不小心,你将收到的错误信息可能会让你很less知道问题实际上是什么。

当你猴子修补一个方法时,你将潜在地破坏依赖于这个行为的下游代码。

当您使用Monkey Patching添加一个新的方法到现有的类时,您可能会打开您无法预见的奇怪边缘情况。

Monkey Patch什么时候可以?

现在说的是,如果你没有真正使用Monkey Patching这样强大的工具,那么没有任何意义。

有些情况下,重新开课是有道理的。

例如,你经常看到Monkey Patches只是简单地添加一个没有副作用的方便方法。 Ruby有一个非常漂亮的语法,所以它可以诱使Monkey Patch一个类来把一些丑陋的方法调用变成更可读的东西。

或者,也许你需要猴子补丁你自己的类。

Monkey Patch很多情况下都可以,但绝对不能成为你的首选武器。

通常情况下,Monkey Patching只是懒惰的开发人员的偏好,而不是实际上为特定问题重构或实现已知的devise模式。

只是因为猴子补丁提供了一个简单的解决scheme,并不意味着你应该总是走这条路。

http://culttt.com/2015/06/17/what-is-monkey-patching-in-ruby/

在Python中,monkeypatching很多时候被认为是一种尴尬的performance:“我不得不把这个类encryption,因为……”(我在处理Zope时遇到了这个问题,文中提到)。 用来说,有必要抓住一个上游类,并在运行时修复它,而不是游说在实际的类中修复不需要的行为或修复它们在一个子类中。 根据我的经验,ruby人不会谈论那么多的monkeypatching,因为它不被认为特别糟糕甚至值得注意(因此“鸭子冲”)。 很明显,你必须小心改变将用在其他依赖项中的方法的返回值,但是以active_support和方面所做的方式向类中添加方法是非常安全的。

通常这是指使用Ruby开放类进行临时更改,通常使用低质量的代码。

关于这个问题的良好后续行动:

http://www.infoq.com/articles/ruby-open-classes-monkeypatching