我还是Ruby的新手(阅读Pickaxe并将大部分时间花在irb
上),现在我知道可以在Ruby中修补类,我想知道什么时候可以接受这样做,特别是修补Ruby的基类是否可以接受。例如:我回答了另一个Ruby问题here,海报想知道如何从DateTime
中减去小时数。由于DateTime
类似乎没有提供此功能,因此我发布了一个答案,可以将DateTime
和Fixnum
类作为可能的解决方案。这是我提交的代码:
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}}运营商的功能。
我能想到的唯一替代方案是:
1。只需动态创建一个新的-
对象,在调用DateTime
时计算新的小时值
new
的 2。编写一个接受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)
的实用程序方法以及从中减去的小时数
基本上,只是方法(1)的包装:
DateTime
第3。向def subtract_hours(date, hours)
return DateTime.new(date.year, date.month, date.day, date.hour - hours, date.min, date.sec)
end
添加新方法,而不是更改DateTime
的现有行为
也许是一个可以与#-()
补丁一起使用的新DateTime#less
方法,以允许这样的语法:
Fixnum#hours
但是,正如我已经提到过的,我采用了修补方法,因为我认为它会产生更具表现力的语法。
我的方法有什么问题,或者我应该使用3种替代方案中的一种(或者我没想过的另一种方法)来做到这一点?我觉得补丁正在成为我对Ruby问题的新“锤子”,所以我想得到一些关于我是否以“Ruby方式”做事的反馈。
答案 0 :(得分:18)
我的个人答案,简而言之:the core-class patching hammer should be at the bottom of your toolbox。您可以使用许多其他技术,几乎在所有情况下,它们都足够,更清洁,更多sustainable。
但这实际上取决于您编码的环境。如果这是一个个人项目 - 确定,贴心你的内容!当您在很长一段时间内与大量程序员一起工作时,问题就开始出现了。在我工作的组织中,我的Ruby代码库超过100KLOC,大约有二十多个开发人员,我们已经开始严厉打击猴子修补,因为我们已经看到它导致头痛,工时浪费行为太常见了。在这一点上,我们几乎只能容忍它暂时修补第三方代码,这些代码尚未合并或不会合并我们的源补丁。
答案 1 :(得分:6)
就个人而言,我认为将方法添加到基类是可以接受的,但是修改现有方法的实现是不可接受的。
答案 2 :(得分:5)
最安全的方法是定义自己继承自内置类的类,然后将新内容添加到新类中。
class MyDateTime < DateTime
alias...
def...
但显然现在只有在声明新类的对象时才会获得新的行为。
答案 3 :(得分:0)
我认为它是这样的:如果你真的觉得大多数其他程序员会同意你的补丁,那很好。如果没有,或许您应该改为实现代码库?