Является ли приемлемой практика исправления базовых классов Ruby, таких как Fixnum? - PullRequest
17 голосов
/ 27 октября 2008

Я все еще очень плохо знаком с 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

Я пропатчил классы, потому что думал, что в этом случае это приведет к ясному и краткому синтаксису для вычитания фиксированного количества часов из 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 way» или нет.

Ответы [ 4 ]

18 голосов
/ 27 октября 2008

Мой личный ответ, в двух словах: патч-молот основного класса должен находиться внизу вашей панели инструментов . Вам доступно множество других техник, и почти во всех случаях они достаточны, чисты и более устойчивы .

Хотя это действительно зависит от среды, в которой вы кодируете. Если это личный проект - конечно, патч для вашего сердца! Проблемы начинают возникать, когда вы работаете над большой базой кода в течение длительного периода времени с большой группой программистов. В организации, в которой я работаю, с кодовыми базами Ruby более 100KLOC и с двадцатью или около того разработчиками, мы начали довольно жестко бороться с исправлениями обезьян, потому что мы видели, как это приводит к головокружительным, затрачиваемым человеком часам слишком часто. На данный момент мы в значительной степени допускаем это только для временного исправления стороннего кода, который либо еще не включен, либо не будет включать наши исходные патчи.

6 голосов
/ 27 октября 2008

Лично я считаю приемлемым добавлять методы в базовые классы, но недопустимо изменять реализацию существующих методов.

5 голосов
/ 27 октября 2008

Самый безопасный способ состоит в том, чтобы определить свой собственный класс, который наследуется от встроенного, а затем добавить новый материал в новый класс.

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

Но, очевидно, теперь вы получите новое поведение, только если объявите объекты вашего нового класса.

0 голосов
/ 27 октября 2008

Я думаю, это так: если вы честно чувствуете, что большинство других программистов согласятся с вашими патчами, тогда хорошо. Если нет, возможно, вам следует вместо этого реализовывать библиотеку кода?

...