修补 Ruby 的基类(例如 Fixnum)是否可以接受?

发布于 2024-07-07 17:57:55 字数 2443 浏览 5 评论 0原文

我对 Ruby 还很陌生(阅读 Pickaxe 并将大部分时间花在 irb 上),现在我知道可以在 Ruby 中修补类,我想知道什么时候可以接受这样做,特别是是否可以接受修补 Ruby 的基类。 例如:我在此处回答了另一个 Ruby 问题,其中发帖者想知道如何从 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)

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

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

I am still very new to Ruby (reading through the Pickaxe and spending most of my time in irb), and now that I know it's possible to patch classes in Ruby, I'm wondering when it's acceptable to do so, specifically whether it's acceptable to patch Ruby's base classes. For example: I answered another Ruby question here where the poster wanted to know how to subtract hours from a DateTime. Since the DateTime class doesn't seem to provide this functionality, I posted an answer that patches the DateTime and Fixnum classes as a possible solution. This is the code I submitted:

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

I patched the classes because I thought in this instance it would result in a clear, concise syntax for subtracting a fixed number of hours from a DateTime. Specifically, you could do something like this as a result of the above code:

five_hours_ago = DateTime.now - 5.hours

Which seems to be fairly nice to look at and easy to understand; however, I'm not sure whether it's a good idea to be messing with the functionality of DateTime's - operator.

The only alternatives that I can think of for this situation would be:

1. Simply create a new DateTime object on-the-fly, computing the new hour value in the call to 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. Write a utility method that accepts a DateTime and the number of hours to subtract from it

Basically, just a wrapper around method (1):

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

3. Add a new method to DateTime instead of changing the existing behavior of #-()

Perhaps a new DateTime#less method that could work together with the Fixnum#hours patch, to allow syntax like this:

date.less(5.hours)

However, as I already mentioned, I took the patching approach because I thought it resulted in a much more expressive syntax.

Is there anything wrong with my approach, or should I be using one of the 3 alternatives (or another one I haven't thought of) in order to do this? I have the feeling that patching is becoming my new 'hammer' for problems in Ruby, so I'd like to get some feedback on whether I'm doing things the "Ruby way" or not.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

习惯成性 2024-07-14 17:57:55

我个人的回答,简而言之:核心级补丁锤应该位于工具箱底部。 还有很多其他技术可供您使用,并且几乎在所有情况下它们都足够、更干净且更可持续

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

My personal answer, in a nutshell: the core-class patching hammer should be at the bottom of your toolbox. There are a lot of other techniques available to you, and in almost all cases they are sufficient, cleaner, and more sustainable.

It really depends on the environment in which you are coding, though. If it's a personal project - sure, patch to your heart's content! The problems begin to arise when you are working on a large codebase over a long period of time with a large group of programmers. In the organization I work for, which has Ruby codebases of over 100KLOC and and twenty or so developers, we have started to crack down pretty hard on monkey patching, because we've seen it lead to head-scratching, man-hour wasting behavior far too often. At this point we pretty much only tolerate it for temporarily patching third-party code which either hasn't yet incorporated or won't incorporate our source patches.

ヤ经典坏疍 2024-07-14 17:57:55

就我个人而言,我认为向基类添加方法是可以接受的,但修改现有方法的实现是不可接受的。

Personally, I think it's acceptable to add methods to the base classes, but unacceptable to modify the implementation of existing methods.

陌上青苔 2024-07-14 17:57:55

最安全的方法是定义您自己的类,该类继承自内置类,然后将您的新内容添加到您的新类中。

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

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

The safest way is to define your own class that inherits from the built-in one, then add your new stuff to your new class.

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

But obviously now you only get the new behavior if you declare objects of your new class.

三生路 2024-07-14 17:57:55

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

I think its like this: If you honestly feel that most other programmers would agree with your patches, then fine. If not, perhaps you should instead be implementing a code library?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文