【问题标题】:Is it acceptable practice to patch Ruby's base classes, such as Fixnum?修补 Ruby 的基类(例如 Fixnum)是否可以接受?
【发布时间】:2010-09-19 07:51:26
【问题描述】:

我对 Ruby 还是很陌生(阅读 Pickaxe 并且大部分时间都在 irb),现在我知道可以在 Ruby 中修补类,我想知道什么时候可以这样做,特别是修补 Ruby 的基类是否可以接受。例如:我回答了另一个 Ruby 问题 here,发帖人想知道如何从 DateTime 中减去小时数。由于DateTime 类似乎不提供此功能,因此我发布了一个答案,将DateTimeFixnum 类作为可能的解决方案进行修补。这是我提交的代码:

require 'date'

# A placeholder class for holding a set number of hours.
# Used so we can know when to change the behavior
# of DateTime#-() by recognizing when hours are explicitly passed in.

class Hours
   attr_reader :value

   def initialize(value)
      @value = value
   end
end

# Patch the #-() method to handle subtracting hours
# in addition to what it normally does

class DateTime

   alias old_subtract -

   def -(x) 
      case x
        when Hours; return DateTime.new(year, month, day, hour-x.value, min, sec)
        else;       return self.old_subtract(x)
      end
   end

end

# Add an #hours attribute to Fixnum that returns an Hours object. 
# This is for syntactic sugar, allowing you to write "someDate - 4.hours" for example

class Fixnum
   def hours
      Hours.new(self)
   end
end

我修补了这些类,因为我认为在这种情况下,它会产生一种清晰、简洁的语法,用于从 DateTime 中减去固定的小时数。具体来说,您可以根据上述代码执行以下操作:

five_hours_ago = DateTime.now - 5.hours

这看起来很好看,也很容易理解;但是,我不确定弄乱DateTime- 运算符的功能是否是个好主意。

对于这种情况,我能想到的唯一选择是:

1.只需即时创建一个新的 DateTime 对象,在对 new 的调用中计算新的小时值

new_date = DateTime.new(old_date.year, old_date.year, old_date.month, old_date.year.day, old_date.hour - hours_to_subtract, date.min, date.sec)


2.编写一个实用方法,接受 DateTime 和从中减去的小时数

基本上,只是方法 (1) 的包装:

def subtract_hours(date, hours)
  return DateTime.new(date.year, date.month, date.day, date.hour - hours, date.min, date.sec)
end


3.向DateTime 添加新方法,而不是更改#-() 的现有行为

也许一个新的DateTime#less 方法可以与Fixnum#hours 补丁一起使用,以允许这样的语法:

date.less(5.hours)

但是,正如我已经提到的,我采用了修补方法,因为我认为它会产生更具表现力的语法。

我的方法有什么问题吗,或者我应该使用 3 种替代方法中的一种(或我没有想到的另一种)来做到这一点?我感觉打补丁正在成为我解决 Ruby 问题的新“锤子”,所以我想就我是否以“Ruby 方式”做事得到一些反馈。

【问题讨论】:

    标签: ruby monkeypatching


    【解决方案1】:

    我个人的回答,简而言之:the core-class patching hammer should be at the bottom of your toolbox。还有很多其他技术可供您使用,并且几乎在所有情况下它们都足够、更干净,而且更多sustainable

    不过,这实际上取决于您编码的环境。如果这是一个个人项目——当然,随心所欲!当您与一大群程序员长时间处理大型代码库时,问题开始出现。在我工作的组织中,拥有超过 100KLOC 的 Ruby 代码库和大约 20 名开发人员,我们已经开始严厉打击猴子补丁,因为我们已经看到它会导致令人头疼的、浪费工时的行为太频繁了。在这一点上,我们几乎只能容忍它临时修补尚未合并或不会合并我们的源补丁的第三方代码。

    【讨论】:

      【解决方案2】:

      我个人认为在基类中添加方法是可以接受的,但是修改现有方法的实现是不能接受的。

      【讨论】:

      • 这里的问题是一个人的添加是另一个人的冲突。我无法告诉你我见过多少次(略有不同)String#/() 的实现。
      • 如果每个人都认为他们可以向基类添加方法,那么当人们并行执行此操作时,就会产生冲突。当你控制所有源时可能无关紧要,但当你的库不控制时,事情就会变得非常奇怪。
      【解决方案3】:

      最安全的方式是定义你自己的继承自内置类的类,然后将你的新东西添加到你的新类中。

      class MyDateTime < DateTime
        alias...
        def...
      

      但显然,现在只有声明新类的对象才能获得新行为。

      【讨论】:

      【解决方案4】:

      我想是这样的:如果你真的觉得大多数其他程序员会同意你的补丁,那很好。如果没有,也许你应该实现一个代码库?

      【讨论】:

      • 我并没有真正打算将我的补丁推送到整个 Ruby 社区。我假设这将是本地更改。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-29
      • 2016-10-12
      • 2023-03-24
      • 1970-01-01
      • 2019-08-25
      相关资源
      最近更新 更多