"猴子补丁"真的那么糟糕吗?

mae*_*ics 20 javascript ruby oop monkeypatching language-design

像Ruby和JavaScript这样的语言有开放类,允许你修改偶数核心类的接口,如数字,字符串,数组等.显然,这样做可能会使熟悉API的其他人感到困惑,但是有充分的理由要避免使用它们. ,假设您正在添加到界面而不是更改现有行为?

例如,将一个Array.map实现添加到未实现ECMAScript第5版的Web浏览器(如果您不需要所有jQuery)可能会很好.或者你的Ruby数组可能会受益于使用"inject"的"sum"方便方法.只要更改被隔离到您的系统(例如,不是您发布用于分发的软件包的一部分),是否有充分的理由不利用此语言功能?

JUS*_*ION 24

与编程工具箱中的许多工具一样,修补猴子可以用于善恶.问题是总的来说,这些工具往往最常用.根据我对Ruby的经验,平衡在很大程度上取决于"邪恶"方面.

那么猴子修补的"邪恶"用法是什么?好吧,猴子修补一般让你对主要的,可能不可识别的冲突敞开大门.我上课了A.我有一些猴子修补模块MB,补丁A包括method1,method2method3.我有另一个猴子修补模块MC,也补丁A包括method2,method3method4.现在我陷入困境.我调用instance_of_A.method2:调用哪个方法?答案可能取决于很多因素:

  1. 我以哪种顺序引入了修补模块?
  2. 补丁是否适用于某种有条件的情况?
  3. AAAAAAARGH!蜘蛛正在从内部吃掉我的眼球!

好的,所以#3可能有点过于戏剧性....

无论如何,这是猴子修补的问题:可怕的冲突问题.鉴于通常支持它的语言的高度动态性,你已经面临很多潜在的"远距离的怪异行动"问题; 猴子修补只是增加了这些.

如果你是一个负责任的开发人员,可以使用猴子修补程序.不幸的是,IME,往往发生的事情是,有人看到猴子修补,并说,"甜!我只是猴子补丁,而不是检查是否其他机制可能不合适." 这种情况大致类似于Lisp代码库,这些代码库是由人们在考虑将其作为一个函数进行操作之前创建的.


Mic*_*ohl 19

维基百科简要总结了猴子修补的陷阱:

http://en.wikipedia.org/wiki/Monkey_patch#Pitfalls

一切都有时间和地点,也适合猴子修补.经验丰富的开发人员可以掌握许多技术,并了解何时使用它们.它很少是一种技术本身就是"邪恶的",只是不加思索地使用它.


Cer*_*nce 6

关于 JavaScript:

假设您要添加到界面而不更改现有行为,是否有充分的理由避免它?

是的。最坏的情况是,即使您不改变现有行为,也可能会损坏该语言未来的语法。

这正是Array.prototype.flatten和所发生的情况Array.prototype.contains。简而言之,规范是为这些方法编写的,他们的提案进入了第 3 阶段,然后浏览器开始发布它。Array但是,在这两种情况下,我们发现有些古老的库用自己的方法修补了内置对象,这些方法与新方法同名,并且具有不同的行为;结果,网站崩溃了,浏览器不得不退出新方法的实现,并且必须编辑规范。(这些方法已重命名。)

Array 如果您像在自己的浏览器、自己的计算机上那样改变内置对象,那就没问题。(这对于用户脚本来说是一项非常有用的技术。)如果您改变面向公众的网站上的内置对象,那就不太好 - 它最终可能会导致如上所述的问题。如果您碰巧控制了一个大网站(例如 stackoverflow.com)并且您改变了一个内置对象,那么您几乎可以保证浏览器将拒绝实现破坏您网站的新功能/方法(因为这样该浏览器的用户将不会能够使用您的网站,并且他们更有可能迁移到其他浏览器)。(有关规范编写者和浏览器制造者之间此类交互的解释,请参阅此处)

综上所述,关于您问题中的具体示例:

例如,向未实现 ECMAScript 第五版的 Web 浏览器添加 Array.map 实现可能会很好

这是一种非常常见且值得信赖的技术,称为polyfill

Polyfill 是在不支持该功能的 Web 浏览器上实现该功能的代码。最常见的是,它指的是实现 HTML5 Web 标准的 JavaScript 库,该标准可以是旧浏览器上的既定标准(受某些浏览器支持),也可以是现有浏览器上的提议标准(不受任何浏览器支持)

例如,如果您为 编写了一个完全符合官方第 4 阶段规范的Array.prototype.map填充程序(或者,采用一个较新的示例,为)Array.prototype.flatMap,然后运行在尚不具备该规范的浏览器上定义的代码:Array.prototype.flatMap

if (!Array.prototype.flatMap) {
  Array.prototype.flatMap = function(...
    // ...
  }
}
Run Code Online (Sandbox Code Playgroud)

如果您的实现是正确的,那么这完全没问题,并且在整个网络上都很常见,以便过时的浏览器可以理解更新的方法。polyfill.io是此类事情的常见服务。