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

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

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

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

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

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

与编程工具箱中的许多工具一样,猴子修补可以用于善恶。 问题是总的来说,这些工具往往最常用。 根据我对Ruby的经验,平衡在“邪恶”方面占据重要地位。

那么猴子修补的“邪恶”用法是什么? 好吧,猴子修补一般让你对主要的,可能不可识别的冲突敞开大门。 我有一个A级。 我有一些猴子修补模块MB修补A包括method1method2method3 。 我有另一个猴子修补模块MC ,它还修补A以包括方法2,方法method3和方法method4 。 现在我陷入困境。 我调用instance_of_A.method2 :调用哪个方法? 答案可能取决于很多因素:

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

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

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

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

只要更改被隔离到您的系统(例如,不是您发布用于分发的软件包的一部分),是否有充分的理由不利用此语言function?

作为孤立问题的唯一开发人员,扩展或更改本机对象没有问题。 此外,对于较大的项目,这是一个应该做出的团队选择。

我个人不喜欢在javascript中修改本机对象,但这是一种常见的做法,这是一个有效的选择。 如果你要编写一个其他人想要使用的库或代码,我会大大避免它。

然而,允许用户设置配置标志是有效的设计选择,请使用便利方法覆盖本机对象,因为它非常方便。

为了说明JavaScript特定的陷阱。

 Array.protoype.map = function map() { ... }; var a = [2]; for (var k in a) { console.log(a[k]); } // 2, function map() { ... } 

使用ES5可以避免此问题,ES5允许您将非可枚举属性注入对象。

这主要是一个高级别的设计选择,每个人都需要意识到/同意这一点。

使用“猴子修补”来纠正一个特定的已知问题是完全合理的,其中替代方案是等待补丁修复它。 这意味着暂时承担修复某些function的责任,直到您可以部署“正确”,正式发布的修补程序。

Gilad Bracha对Monkey Patching的一个深思熟虑的意见: http : //gbracha.blogspot.com/2008/03/monkey-patching.html

您描述的条件 – 添加(不更改)现有行为,而不是将代码发布到外部世界 – 似乎相对安全。 但是,如果下一版本的Ruby或JavaScript或Rails更改其API,则可能会出现问题。 例如,如果某个未来版本的jQuery检查是否已经定义了Array.map,并假设它是EMCA5Script版本的map,实际上它是你的猴子补丁怎么办?

同样,如果你在Ruby中定义“sum”,有一天你决定要在Rails中使用ruby代码或者将Active Support gem添加到你的项目中。 Active Support还定义了一个sum方法(在Enumerable上),因此存在冲突。