25

在一些个人项目中,我正在慢慢地从 PHP5 迁移到 Python,我目前很喜欢这种体验。在选择走 Python 路线之前,我查看了 Ruby。我从 ruby​​ 社区注意到的是,猴子修补既常见又备受推崇。我还遇到了很多关于调试 ruby​​ 软件试验的恐怖故事,因为有人包含一个相对无害的库来做一些小工作,但它在不告诉任何人的情况下修补了一些大量使用的核心对象。

我选择 Python 的原因之一是它更简洁的语法以及它可以完成 Ruby 可以做的所有事情。Python 使 OO 的点击率比 PHP 更好,我正在阅读越来越多的 OO 原则以增强对这种更好的理解。

今天晚上我一直在阅读关于罗伯特马丁的 SOLID原则:

  • 单一责任原则,
  • 闭原则,
  • L iskov替换原理,
  • 接口隔离原则,以及
  • 依赖倒置原则

我目前达到O软件实体(类、模块、功能等)应该对扩展开放,但对修改关闭

我的头脑在确保 OO 设计的一致性和整个猴子补丁之间的冲突。我知道可以在 Python 中进行猴子修补。我也明白,“pythonic”就是遵循常见的、经过充分测试的、oop 最佳实践和原则。

我想知道的是社区对这两个对立主题的看法;它们如何互操作,何时最好使用一个,是否应该进行猴子修补......希望你能为我提供解决方案。

4

8 回答 8

29

猴子修补(覆盖或修改预先存在的方法)和简单添加新方法之间是有区别的。我认为后者完全没问题,前者应该被怀疑地看待,但我仍然赞成保留它。

我遇到了很多这样的问题,第三方扩展猴子修补了核心库并破坏了一些东西,它们确实很糟糕。不幸的是,它们似乎总是源于第三方扩展开发者走的阻力最小的道路,而不是考虑如何真正正确地构建他们的解决方案。
这很糟糕,但这不仅仅是猴子修补的错,也不是人们有时割伤自己的刀匠的错。

我见过的唯一一次合法的猴子补丁需要是解决第三方或核心库中的错误。仅就这一点而言,它是无价的,如果他们取消了这样做的能力,我真的会感到失望。

我们有一个 C# 程序中的错误的时间表:

  1. 阅读奇怪的错误报告并将问题跟踪到 CLR 库中的小错误。
  2. 花几天时间想出一个解决方法,包括在奇怪的地方捕获异常和大量破坏代码的黑客行为
  3. 当 Microsoft 发布服务包时,花费数天时间来解决 hacky 解决方法

我们有一个rails程序中的一个错误的时间表:

  1. 阅读奇怪的错误报告并跟踪问题到 ruby​​ 标准库中的一个小错误
  2. 花 15 分钟执行小猴子补丁以从 ruby​​ 库中删除错误,并在其周围放置警卫以防止它在错误版本的 ruby​​ 上运行。
  3. 继续正常编码。
  4. 稍后发布下一个 ruby​​ 版本时,只需删除 monkeypatch。

错误修复过程看起来很相似,除了猴子补丁,这是一个 15 分钟的解决方案和一个 5 秒的“提取”,而没有它,痛苦和痛苦随之而来。

PS:下面的例子是“技术上”的monkeypatching,但它是“道德上”的monkeypatching吗?我没有改变任何行为——这或多或少只是在 ruby​​ 中做 AOP ......

class SomeClass
  alias original_dostuff dostuff
  def dostuff
    # extra stuff, eg logging, opening a transaction, etc
    original_dostuff
  end
end
于 2008-11-17T20:23:48.977 回答
5

在我看来,monkeypatching 很有用,但也有可能被滥用。人们倾向于发现它,并觉得它应该在任何情况下都使用,在这种情况下,也许使用 mixin 或其他构造可能更合适。

我不认为这是你应该取缔的东西,它只是 Ruby 人喜欢使用的东西。你可以用 Python 做类似的事情,但社区的立场是事情应该更简单、更明显。

于 2008-11-15T23:47:37.617 回答
4

猴子补丁不是 ruby​​ 显式的,它也通过 javascript 完成,具有负面 (IMO) 效果。

我个人的意见是猴子补丁应该只做

a) 将功能添加到您需要的新版本语言中可用的旧版本语言中。

b)当没有其他“合乎逻辑”的地方时。

有很多简单的方法可以让猴子补丁变得非常糟糕,例如改变基本功能(如ADDITION )的工作方式的能力。

我的立场是,如果你能避免,就这样做。

如果你能以一种很好的方式避免它,向你致敬。

如果你无法避免它,请听取 200 个人的意见,因为你可能只是没有认真考虑过它。

我最讨厌的是扩展函数对象的 mootools 。是的,你可以这样做。而不是人们只是学习 javascript 的工作原理:

setTimeout(function(){ 
    foo(args); 
}, 5000 ); 

每个函数对象都添加了一个新方法(是的,我不是在开玩笑),因此函数现在有了自己的函数。

foo.delay( 5000 , args );

这具有这种废话有效的额外效果:

foo.delay.delay( 500,  [ 500, args ] ); 

就这样无限循环。

结果?你不再有图书馆和语言,你的语言屈从于图书馆,如果图书馆恰好在范围内,你就不再有语言,你不能像学习时那样做事语言,而是必须学习一个的命令子集,以免让它一蹶不振(以过度减速为代价!)

我可以注意到 foo.delay 还返回了一个对象,它有自己的方法,所以你可以这样做

x = foo.delay( 500, args ); 
x.clear(); 

乃至

 x.clear.delay(10); 

这听起来可能过于有用,......但你必须考虑到用于使其可行的大量开销。

 clearTimeout(x); 

太难了!

(免责声明:我使用 moo 已经有一段时间了,并试图忘记它,函数名称/结构可能不正确。这不是 API 参考。请查看他们的网站了解详细信息(抱歉,他们的 API 参考很烂!) )

于 2008-11-15T23:55:05.417 回答
4

Mokeypatching 通常是错误的。创建一个适当的子类并添加方法。

我在生产代码中使用过一次猴子补丁。

问题是 REST 使用 GET、POST、PUT 和 DELETE。但是 Django 测试客户端只提供 GET 和 POST。我已经为 PUT(如 POST)和 DELETE(如 GET)提供了猴子补丁方法。

由于 Django 客户端和 Django 测试驱动程序之间的紧密绑定,似乎最容易对其进行猴子补丁以支持完整的 REST 测试。

于 2008-11-16T01:47:53.880 回答
3

您可能会发现有关 Ruby 的开放类和开放封闭原则的讨论很有启发性。

尽管我喜欢 Ruby,但我觉得猴子补丁是完成工作的最后手段。在所有条件相同的情况下,我更喜欢使用带有函数式编程优点的传统 OO 技术。

于 2008-11-16T01:07:37.533 回答
2

在我看来,monkey-patching 是 AOP 的一种形式。面向方面的设计原则:面向对象设计的教训(PDF) 一文给出了一些关于如何将 SOLID 和其他 OOP 原则应用于 AOP 的想法。

于 2009-03-11T00:02:38.690 回答
1

我的第一个想法是猴子修补违反了 OCP,因为一个类的客户应该能够期望该类能够始终如一地工作。

于 2008-11-16T00:20:02.620 回答
0

猴子修补是完全错误的,恕我直言。我没有遇到过您之前提到的开放/封闭原则,但这是我长期以来一直坚持的原则,我 100% 同意。我认为猴子修补是一种更大范围的代码气味,一种编码哲学的气味。

于 2008-11-16T00:00:31.477 回答