2008-10-27 6 views
17

Todavía soy muy nuevo para Ruby (leyendo el Pico y pasando la mayor parte de mi tiempo en irb), y ahora que sé que es posible parchar clases en Ruby, me pregunto cuándo es aceptable hacerlo, específicamente si es aceptable parchear las clases base de Ruby. Por ejemplo: respondí otra pregunta de Ruby here donde el aficionado quería saber cómo restar horas de DateTime. Como la clase DateTime no parece proporcionar esta funcionalidad, publiqué una respuesta que corrige las clases DateTime y Fixnum como una posible solución. Este es el código que presenté:¿Es aceptable practicar parchear las clases base de Ruby, como 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 

remendé las clases porque pensé que en este caso resultaría en una sintaxis clara, concisa para restar un número fijo de horas de un DateTime. En concreto, se podría hacer algo como esto como resultado del código anterior:

five_hours_ago = DateTime.now - 5.hours 

que parece ser bastante agradable a la vista y fácil de entender; sin embargo, no estoy seguro si es una buena idea estar jugando con la funcionalidad del operador DateTime-.

Las únicas alternativas que puedo imaginar para Sería esta situación:

1. Basta con crear un nuevo objeto en DateTime la marcha, calculando el nuevo valor de la 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. Escribir un método de utilidad que acepta un DateTime y el número de horas para restar de ella

Básicamente, sólo un envoltorio sobre el método (1):

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


3. Añadir un nuevo método para DateTime en lugar de cambiar el comportamiento actual de #-()

Tal vez 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 he mencionado, me tomó el enfoque de parches porque pensé que dio lugar a una sintaxis mucho más expresiva.

¿Hay algo malo con mi enfoque, o debería estar utilizando una de las 3 alternativas (u otra en la que no he pensado) para poder hacer esto? Tengo la sensación de que el parcheo se está convirtiendo en mi nuevo "martillo" para los problemas en Ruby, así que me gustaría obtener algunos comentarios sobre si estoy haciendo las cosas al estilo "Ruby" o no.

Respuesta

18

Mi respuesta personal, en pocas palabras: the core-class patching hammer should be at the bottom of your toolbox. Hay muchas otras técnicas disponibles para usted, y en casi todos los casos son suficientes, más limpias y más sustainable.

Sin embargo, realmente depende del entorno en el que está codificando. Si se trata de un proyecto personal, seguro, parche para su corazón. Los problemas comienzan a surgir cuando trabajas en una gran base de código 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 Ruby de más de 100KLOC y una veintena de desarrolladores, hemos empezado a tomar medidas duras con los parches de los monos, porque hemos visto que esto lleva a un desgaste de la cabeza, a la hora de perder el comportamiento demasiado a menudo En este punto, prácticamente solo lo toleramos por el parche temporal de código de terceros que aún no ha incorporado o no incorporará nuestros parches de origen.

6

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.

+0

En segundo lugar esta opinión. –

+1

El problema aquí es que la suma de una persona es el conflicto de otra persona. No puedo decirte cuántas veces he visto una implementación (ligeramente diferente) de 'String # /()'. – Avdi

+1

Si todos piensan que pueden agregar métodos a una clase base, entonces obtienes conflictos ya que las personas hacen esto en paralelo. Puede que no importe cuando controlas toda la fuente, pero cuando las bibliotecas no controlan, las cosas se ponen realmente raras. –

0

Creo que es así: Si honestamente siente que la mayoría de los otros programadores estarían de acuerdo con sus parches, entonces está bien. Si no, ¿quizás debería implementar una biblioteca de códigos?

+0

No estaba realmente planeando impulsar mis parches en la comunidad de Ruby en general. Estaba asumiendo que esto sería un cambio local. –

5

La forma más segura es definir su propia clase que hereda de la incorporada, luego agregue sus cosas nuevas a su nueva clase.

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

Pero obviamente ahora solo obtienes el nuevo comportamiento si declaras objetos de tu nueva clase.

+0

+1 Eso fue lo suficientemente obvio para que yo no lo piense ;-) –

+0

No se olvide de la delegación/decoración: http://avdi.org/devblog/2008/04/17/sustainable-development-in-ruby- parte-3-delegación / – Avdi

Cuestiones relacionadas