Допустимо ли исправление базовых классов Ruby, таких как Fixnum?

StackOverflow https://stackoverflow.com/questions/238878

  •  04-07-2019
  •  | 
  •  

Вопрос

Я все еще новичок в Ruby (читаю Кирку и провожу большую часть времени в irb), и теперь, когда я знаю, что в 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's - оператор.

Единственные альтернативы, которые я могу придумать для этой ситуации:

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» или нет.

Это было полезно?

Решение

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

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

Другие советы

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top