我仍然是Ruby的新手(阅读Pickaxe并花费大部分时间irb
),现在我知道可以在Ruby中修补类,我想知道什么时候可以接受这样做,特别是它是否可以接受修补Ruby的基类.例如:我在这里回答了另一个Ruby问题,海报想要知道如何减去小时数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
我修补了这些类,因为我认为在这种情况下,它会产生一个清晰,简洁的语法,用于从a中减去固定的小时数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.编写一个实用方法,接受a 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代码库超过100KLOC,大约有二十多个开发人员,我们已经开始严厉打击猴子修补,因为我们已经看到它导致头痛,工时浪费行为太常见了.在这一点上,我们几乎只能容忍它暂时修补第三方代码,这些代码尚未合并或不会合并我们的源补丁.
就个人而言,我认为将方法添加到基类是可以接受的,但是修改现有方法的实现是不可接受的.
该最安全的方法是定义自己的类,从内置的一个继承,然后添加新的东西,新的类.
class MyDateTime < DateTime alias... def...
但显然现在只有在声明新类的对象时才会获得新的行为.