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

|                                                                                                                   关闭。这个问题是基于意见的。它当前不接受答案。                                                      
已邀请:
Wikipedia简要概述了猴子修补的陷阱: http://en.wikipedia.org/wiki/Monkey_patch#陷阱 有一个时间和地点,可以进行猴子修补。经验丰富的开发人员掌握了很多技术,可以学习何时使用它们。本身很少考虑“邪恶”的技术,只是不考虑使用它。     
像编程工具箱中的许多工具一样,猴子补丁可以用于善与恶。问题是,总的来说,此类工具倾向于在哪里使用。根据我对Ruby的经验,平衡在“邪恶”方面很重。 那么,猴子补丁的“邪恶”用途是什么?好吧,一般而言,猴子打补丁会让您对可能发生的,无法诊断的重大冲突敞开大门。我上课
A
。我有某种猴子修补模块
MB
,它修补
A
以包括patches3ѭ,
method2
method3
。我还有另一个猴子修补模块
MC
,它也修补了
A
以包括patches4ѭ,
method3
method4
。现在我陷入困境。我叫
instance_of_A.method2
:调用了谁的方法?答案可能取决于许多因素: 我以什么顺序引入补丁模块? 是立即应用补丁还是在某种条件下应用补丁? AAAAAAARGH!蜘蛛正在从内部蚕食我的眼球! 好吧,所以#3可能有点戏剧性。 无论如何,那是猴子修补的问题:可怕的冲突问题。鉴于通常支持该语言的语言具有高度动态性,因此您已经面临许多潜在的“远距离动作”。猴子补丁只是增加了这些。 如果您是负责任的开发人员,那么可以使用猴子补丁程序就很好了。不幸的是,IME,发生的往往是有人看到猴子在打补丁,并说:“甜!我只是在猴子打补丁,而不是检查其他机制是否不合适。”这种情况大致类似于Lisp代码库,该库是由人们在只考虑将其作为功能之前就接触宏而创建的。     
  只要更改被隔离到   您的系统(例如不属于   您发布的软件包   分布)是否有充分的理由   不要利用这种语言   特征? 作为一个孤立的问题的单独开发人员,扩展或更改本机对象没有问题。同样,在较大的项目中,应该选择团队。 我个人不喜欢更改javascript中的本机对象,但这是一种常见做法,也是一种有效的选择。如果您打算编写供他人使用的库或代码,则我会尽量避免使用它。 但是,允许用户设置配置标志是一种有效的设计选择,该标志指出请使用您的便捷方法覆盖本机对象,因为这样做很方便。 为了说明特定于JavaScript的陷阱。
Array.protoype.map = function map() { ... };

var a = [2];
for (var k in a) {
    console.log(a[k]);
} 
// 2, function map() { ... }
通过使用ES5可以避免此问题,该ES5允许您将不可枚举的属性注入到对象中。 这主要是一个高级设计选择,每个人都需要了解/同意这一点。     
使用“猴子补丁”来纠正一个特定的已知问题是完全合理的,在这种情况下,替代方案是等待补丁对其进行修复。这意味着临时负责修复某些问题,直到可以部署的“适当”正式发布的修复为止。 吉拉德·布拉恰(Gladad Bracha)对猴子补丁的意见:http://gbracha.blogspot.com/2008/03/monkey-patching.html     
您描述的条件-添加(不更改)现有行为,并且不将代码发布给外界-似乎相对安全。但是,如果下一版本的Ruby或JavaScript或Rails更改其API,可能会出现问题。例如,如果某个将来的jQuery版本检查是否已定义Array.map并假设它是map的EMCA5Script版本,而实际上是您的猴子补丁,那该怎么办? 同样,如果您在Ruby中定义\“ sum \”,又有一天您决定要在Rails中使用该ruby代码或将Active Support gem添加到您的项目中,该怎么办? Active Support还定义了sum方法(在Enumerable上),因此存在冲突。     
关于Javascript:   有充分的理由避免这种情况吗,假设您要添加到接口中而不更改现有行为? 是。最坏的情况是,即使您不改变现有行为,也可能会破坏该语言的未来语法。 这正是ѭ13和ѭ14发生的情况。简而言之,为这些方法编写了规范,他们的建议进入了第3阶段,然后浏览器开始发布它。但是,在这两种情况下,都发现有一些古老的库用它们自己的方法对内置的“ 15”对象进行了修补,它们的名称与新方法的名称相同,并且行为不同。结果,网站崩溃了,浏览器不得不退出对新方法的实现,并且必须对规范进行编辑。 (方法已重命名。) 如果您在自己的浏览器中或在您自己的计算机上更改诸如ѭ15之类的内置对象,就可以了。 (这对于用户脚本来说是一种非常有用的技术。)如果您在面向公众的站点上对内置对象进行了变异,则效果可能会更差-最终可能会导致上述问题。如果您碰巧控制了一个大型网站(例如stackoverflow.com)并更改了内置对象,则几乎可以保证浏览器将拒绝实施破坏您网站的新功能/方法(因为该浏览器的用户不会能够使用您的网站,并且它们很可能会迁移到其他浏览器)。 (有关规范编写者和浏览器制造商之间这种相互作用的说明,请参见此处) 关于您问题中的特定示例,所有这些说明如下:   例如,将Array.map实现添加到未实现ECMAScript第5版的Web浏览器可能会很好 这是一种非常常见且值得信赖的技术,称为polyfill。   polyfill是在不支持该功能的Web浏览器上实现功能的代码。大多数情况下,它是指一个JavaScript库,该库实现HTML5 Web标准,或者是旧版浏览器上的已建立标准(某些浏览器支持),或者是现有浏览器上的建议标准(不受任何浏览器支持)。 例如,如果您为
Array.prototype.map
(或举一个较新的示例,为
Array.prototype.flatMap
)编写了一个与官方Stage 4规范完全一致的polyfill,然后在没有它的浏览器中运行了定义
Array.prototype.flatMap
的代码已经:
if (!Array.prototype.flatMap) {
  Array.prototype.flatMap = function(...
    // ...
  }
}
如果您的实现是正确的,那将是非常好的事情,并且通常在整个Web上都可以完成,因此过时的浏览器可以理解更新的方法。 polyfill.io是此类事情的常用服务。     

要回复问题请先登录注册