¿Es una práctica aceptable parchear las clases base de Ruby, como Fixnum?
-
04-07-2019 - |
Pregunta
Todavía soy muy nuevo en Ruby (leyendo el Pickaxe y pasando la mayor parte del tiempo en irb
), y ahora que sé que es posible parchear clases en Ruby, me pregunto cuándo. es aceptable hacerlo, específicamente si es aceptable parchar las clases base de Ruby. Por ejemplo: respondí a otra pregunta de Ruby aquí donde el cartel quería saber cómo restar horas de un DateTime
. Como la clase DateTime
no parece proporcionar esta funcionalidad, publiqué una respuesta que parchea las clases DateTime
y Fixnum
como una posible solución. Este es el código que envié:
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
Parché las clases porque pensé que en este caso daría como resultado una sintaxis clara y concisa para restar un número fijo de horas de un DateTime
. Específicamente, podría hacer algo como esto como resultado del código anterior:
five_hours_ago = DateTime.now - 5.hours
Que parece ser bastante agradable de ver y fácil de entender; sin embargo, no estoy seguro de si es una buena idea estar jugando con la funcionalidad del operador -
de DateTime
.
Las únicas alternativas que se me ocurren para esta situación serían:
1. Simplemente cree un nuevo objeto DateTime
sobre la marcha, calculando el nuevo valor de hora en la llamada a 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. Escriba un método de utilidad que acepte un DateTime
y la cantidad de horas que se le restarán
Básicamente, solo un método de envolver alrededor de (1):
def subtract_hours(date, hours)
return DateTime.new(date.year, date.month, date.day, date.hour - hours, date.min, date.sec)
end
3. Agregue un nuevo método a DateTime
en lugar de cambiar el comportamiento existente de #-()
Quizás un nuevo método DateTime # less
que podría funcionar junto con el parche Fixnum # hours
, para permitir una sintaxis como esta:
date.less(5.hours)
Sin embargo, como ya mencioné, adopté el enfoque de parcheo porque pensé que daba como resultado una sintaxis mucho más expresiva.
¿Hay algún problema con mi enfoque o debo utilizar una de las 3 alternativas (u otra en la que no haya pensado) para hacer esto? Tengo la sensación de que la aplicación de parches se está convirtiendo en mi nuevo 'martillo' para problemas en Ruby, por lo que me gustaría saber si estoy haciendo las cosas de la manera '' Ruby '' o no.
Solución
Mi respuesta personal, en pocas palabras: el martillo parcheador de clase principal debería estar en la parte inferior de su caja de herramientas . Hay muchas otras técnicas disponibles para usted, y en casi todos los casos son suficientes, más limpias y más sostenible .
Sin embargo, realmente depende del entorno en el que estés codificando. Si es un proyecto personal, claro, ¡parchelo al contenido de su corazón! Los problemas comienzan a surgir cuando se trabaja en una base de código grande durante un largo período de tiempo con un gran grupo de programadores. En la organización para la que trabajo, que tiene bases de código de Ruby de más de 100KLOC y una veintena de desarrolladores, hemos empezado a tomar medidas enérgicas contra los parches de monos, porque hemos visto que esto se traduce en un comportamiento de despeje de cabeza y pérdida de horas de trabajo. con demasiada frecuencia En este punto, prácticamente solo lo toleramos para parchear temporalmente el código de terceros que aún no ha incorporado o no incorporará nuestros parches de origen.
Otros consejos
Personalmente, creo que es aceptable agregar métodos a las clases base, pero no es aceptable modificar la implementación de los métodos existentes.
La forma más segura es definir su propia clase que se hereda de la incorporada, luego agregar sus nuevas cosas a su nueva clase.
class MyDateTime < DateTime
alias...
def...
Pero obviamente ahora solo obtienes el nuevo comportamiento si declaras objetos de tu nueva clase.
Creo que es así: si honestamente sientes que la mayoría de los otros programadores estarían de acuerdo con tus parches, entonces está bien. Si no es así, ¿quizás debería implementar una biblioteca de códigos?