Monkey Patch猴子补丁编程方式及其在Ruby中的运用

所属分类: 脚本专栏 / ruby专题 阅读数: 1736
收藏 0 赞 0 分享

何谓猴子补丁(Monkey Patch)?在动态语言中,不修改源代码而对功能进行追加和变更。

使用猴子补丁的目的:
1、追加功能
2、功能变更
3、修正程序错误
4、增加钩子,在执行某个方法的同时执行一些其他的处理,如打印日志,实现AOP等,
5、缓存,在计算量很大,结算之后的结果可以反复使用的情况下,在一次计算完成之后,对方法进行替换可以提高处理速度。

Ruby的类都是开放类,即在类定义之后还可以任意添加内容, 这就使得在Ruby中使用猴子补丁变得特别容易了。另外,Ruby还提供了对方法、类和模块的进行操作的功能,让我们使用猴子补丁更加得心应手。Ruby提供的基本功能如下:
      alias:给方法另起别名
      include:引入其他模块的方法
      remove_method: 取消本类中的方法
      undef:取消方法 
     
在 Ruby 中使用 Monkey Patch
我当时遇到的场景是这样的:

我司使用第三方库 fog 进行 EC2 的操作。创建实例等很多命令都需要设置实例类型这个参数。在 fog 里,EC2 的所有类型都定义在 fog/aws/models/compute/flavors.rb 的 FLAVORS 数组里。如果设置的类型不在 FLAVORS 数组里,fog 都会视作是无效的参数而报错。

后来,亚马逊发布了新的实例类型 D2。虽然 Ruby 的第三方社区非常活跃,但是 fog 的开发社区还是没有及时添加 D2 到 flavors.rb 里;而我司的工作又迫切需要使用 D2 类型的实例。

背景交待完毕,接下来看看有什么样的解决方法。

方法一:我们可以向 fog 提交一个 Pull Request 来添加新类型。

但是这个方法行不通。我们使用的 knife-ec2 对 fog 的版本依赖必须是 1.25.*,但是 fog 已经更新到了 1.31.0,而且 fog 从 1.27.0 开始结构上有很大的变化。显然,我们不可能再等 knife-ec2 升级支持新版本的 fog,所以我们提交 Pull Request 更新 fog 不能解决问题。

方法二:手动更新旧版 fog 既然不能使用最新版的 fog,我们可以手动编辑 1.25 版的 fog,再打包成 Gem 使用。这个方法比前一个方法更容易操作,但是带来的问题时不易于维护。为了一个极小的改动,把自己的代码加入到第三方库中总是让人觉得不够「干净」。

最后,在同事的指点下,我采用了第三种方法,即 Monkey Patch。我在我司的 Ruby 项目里添加了一个文件 lib/PROJECT_NAME/monkey_patches/flavors.rb,接着在文件中添加以下代码来修改 fog/aws/models/compute/flavors:

require 'fog/aws/models/compute/flavors'

class Object

 def redef_without_warning(const, value)
  mod = self.is_a?(Module) ? self : self.class
  mod.send(:remove_const, const) if mod.const_defined?(const)
  mod.const_set(const, value)
 end
end

module Fog
 module Compute
  class AWS
   NEW_FLAVORS = FLAVORS + [
    {
     :id => "d2.xlarge",
     ...
    },
    {
     :id => "d2.2xlarge",
     ...
    },
    {
     :id => "d2.4xlarge",
     ...
    },
    {
     :id => "d2.8xlarge",
     ...
    }
   ]

   redef_without_warning :FLAVORS, NEW_FLAVORS

  end
 end
end

总结
通过在自己的代码中添加一个 Monkey patch,我们成功地实现了向 fog 中动态添加新实例类型。我司终于可以使用 fog 创建 D2 类型的机器了;而且这个方法改动的代码量最小,也更加容易维护。

Monkey Patch 并非是完美的解决方法,它会引入一些陷阱。所以这个技巧在软件工程领域还有一些争议。不过,我还是觉得 Monkey Patch 是一个不错的零时性解决方法。

更多精彩内容其他人还在看

Ruby 面向对象知识总结

这篇文章主要介绍了Ruby 面向对象的的相关资料,文中讲解非常详细,实例代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
收藏 0 赞 0 分享

Ruby中任务构建工具rake的入门学习教程

这篇文章主要介绍了Ruby中任务构建工具rake的入门学习教程,讲解了包括命名空间和默认任务的执行等基础知识,需要的朋友可以参考下
收藏 0 赞 0 分享

Ruby程序中发送基于HTTP协议的请求的简单示例

这篇文章主要介绍了Ruby程序中发送基于HTTP协议的请求的简单示例,包括对HTTPS请求的介绍,需要的朋友可以参考下
收藏 0 赞 0 分享

Ruby的字符串与数组求最大值的相关问题讨论

这篇文章主要介绍了Ruby中的字符串与数组求最大值的相关问题,文中还提到了sort排序方法的相关用法,需要的朋友可以参考下
收藏 0 赞 0 分享

Ruby中Time对象的常用函数总结

这篇文章主要介绍了Ruby中Time对象的常用函数总结,包括Ruby中一些实用的时间算法,需要的朋友可以参考下
收藏 0 赞 0 分享

详解Ruby中正则表达式对字符串的匹配和替换操作

这篇文章主要介绍了Ruby中正则表达式对字符串的匹配和替换操作,包括对结果分组和一些特殊全局变量的介绍,需要的朋友可以参考下
收藏 0 赞 0 分享

深入剖析Ruby设计模式编程中对命令模式的相关使用

这篇文章主要介绍了Ruby设计模式编程中对命令模式的相关使用,文中还讲到了关于观察者模式和命令模式的一些概念区别,需要的朋友可以参考下
收藏 0 赞 0 分享

实例解析Ruby设计模式开发中对观察者模式的实现

这篇文章主要介绍了实例解析Ruby设计模式开发中对观察者模式的实现,Ruby中自带的observer类自然是绝佳的使用示例,需要的朋友可以参考下
收藏 0 赞 0 分享

设计模式中的观察者模式在Ruby编程中的运用实例解析

这篇文章主要介绍了设计模式中的观察者模式在Ruby编程中的运用实例解析,观察者模式中主张设立观察者对象来降低对象之间的耦合,需要的朋友可以参考下
收藏 0 赞 0 分享

解析proxy代理模式在Ruby设计模式开发中的运用

这篇文章主要介绍了proxy代理模式在Ruby设计模式开发中的运用,通过代理模式以客户透明的形式可以动态地为目标对象加以行为控制,需要的朋友可以参考下
收藏 0 赞 0 分享
查看更多