¿Qué significa exactamente ''Monkey Patching'' en Ruby?
terminology monkeypatching (8)
Según Wikipedia, un parche de mono es:
una forma de ampliar o modificar el código de tiempo de ejecución de lenguajes dinámicos [...] sin alterar el código fuente original.
La siguiente declaración de la misma entrada me confundió:
En Ruby, el término parche de mono no se entendió como cualquier modificación dinámica de una clase y, a menudo, se utiliza como sinónimo de modificación dinámica de cualquier clase en tiempo de ejecución.
Me gustaría saber el significado exacto de parche de monos en Ruby. ¿Está haciendo algo como lo siguiente, o es algo más?
class String
def foo
"foo"
end
end
El parche de mono es cuando reemplaza los métodos de una clase en el tiempo de ejecución ( sin agregar nuevos métodos como otros han descrito).
Además de ser una forma muy obvia y difícil de depurar para cambiar el código, no escala; a medida que más y más módulos inician métodos de parche de monos, aumenta la probabilidad de que los cambios se aceleren unos a otros.
Esto es parche de monos:
class Float
def self.times(&block)
self.to_i.times { |i| yield(i) }
remainder = self - self.to_i
yield(remainder) if remainder > 0.0
end
end
Ahora me imagino que esto podría ser útil a veces, pero imagine si viera la rutina.
def my_method(my_special_number)
sum = 0
my_special_number.times { |num| sum << some_val ** num }
sum
end
Y se rompe solo ocasionalmente cuando se llama. Para aquellos que prestan atención, ya saben por qué, pero imaginen que no sabían que el tipo flotante tiene un .times
clase .times
y automáticamente asumieron que my_special_number
es un número entero. Cada vez que el parámetro es un número entero, entero o flotante, funcionaría bien (se pasan los enteros enteros excepto cuando hay un resto de punto flotante). ¡Pero pase un número con cualquier cosa en el área decimal y se romperá con seguridad!
Imagínese con qué frecuencia esto podría suceder con sus gemas, complementos de Rails e incluso con sus propios compañeros de trabajo en sus proyectos. Si hay uno o dos métodos pequeños como este, podría tomar algún tiempo encontrarlos y corregirlos.
Si se pregunta por qué se rompe, tenga en cuenta que la sum
es un número entero y que un resto de punto flotante podría pasarse; Además, el signo exponencial solo funciona cuando los tipos son iguales. Entonces, puede pensar que es fijo, porque convirtió los números molestos en carrozas ... solo para descubrir que la suma no puede tomar el resultado de coma flotante.
La respuesta corta es que no hay un significado "exacto", porque es un término nuevo, y diferentes personas lo usan de manera diferente. Eso al menos se puede discernir del artículo de Wikipedia. Hay algunos que insisten en que solo se aplica al código de "tiempo de ejecución" (supongo que en las clases incorporadas) mientras que otros lo usarían para referirse a la modificación en tiempo de ejecución de cualquier clase.
Personalmente, prefiero la definición más inclusiva. Después de todo, si tuviéramos que usar el término para la modificación de las clases integradas solamente, ¿cómo nos referiríamos a la modificación del tiempo de ejecución de todas las otras clases? Lo importante para mí es que hay una diferencia entre el código fuente y la clase de ejecución real.
En Ruby, el término parche de mono no se entendió como cualquier modificación dinámica de una clase y, a menudo, se utiliza como sinónimo de modificación dinámica de cualquier clase en tiempo de ejecución.
La declaración anterior afirma que el uso de Ruby es incorrecto, pero los términos evolucionan, y eso no siempre es malo.
Por lo general, se refiere a cambios ad-hoc, usando clases abiertas de Ruby, frecuentemente con código de baja calidad.
Buen seguimiento del tema:
http://www.infoq.com/articles/ruby-open-classes-monkeypatching
La mejor explicación que escuché sobre Monkey patching / Duck-Punching es la de Patrick Ewing en RailsConf 2007
... si camina como un pato y habla como un pato, es un pato, ¿verdad? Entonces, si este pato no te está dando el sonido que deseas, tienes que golpear ese pato hasta que regrese lo que esperas.
Uno de los aspectos más poderosos de Ruby es la posibilidad de volver a abrir cualquier clase y cambiar sus métodos.
Sí, es cierto, puede reabrir cualquier clase y cambiar la forma en que funciona. Esto incluye las clases estándar de Ruby, como
String, Array or Hash!
Ahora esto es obviamente tan peligroso como parece. Ser capaz de cambiar el resultado esperado de un método puede causar todo tipo de comportamiento extraño y es difícil rastrear errores.
Pero, no obstante, la capacidad de "Monkey Patch" de cualquier clase es extremadamente poderosa. Ruby es como un cuchillo afilado, puede ser extremadamente efectivo, pero por lo general es tu culpa si te cortas.
Primero, agregaremos un método útil para generar algunos textos de Lorem Ipsum:
class String
def self.lipsum
"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
end
end
En este ejemplo he reabierto la clase de núcleo String
y he añadido un método de clase lipsum.
String.lipsum
=> "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
Sin embargo, no solo podemos agregar métodos a la clase String
central, sino que también podemos modificar el comportamiento de los métodos existentes.
class String
def upcase
self.reverse
end
end
En este ejemplo, estamos secuestrando el método upcase
y llamando al método reverse
lugar.
"hello".upcase
=> "olleh"
Como puede ver, es increíblemente fácil agregar o modificar métodos en una clase existente, incluso cuando no es dueño de esa clase o es parte del núcleo de Ruby.
¿Cuándo deberías usar Monkey Patching?
Raramente.
Ruby nos proporciona una gran cantidad de herramientas poderosas para trabajar. Sin embargo, solo porque una herramienta sea poderosa, no la convierte en la herramienta adecuada para el trabajo.
Monkey Patching en particular es una herramienta extremadamente poderosa. Sin embargo, una herramienta poderosa en las manos equivocadas causará cantidades interminables de dolor y sufrimiento.
Cada vez que Monkey Patch una clase potencialmente estás creando un dolor de cabeza en algún momento en el futuro cuando las cosas van mal.
Las clases que han sido parcheadas con mono son más difíciles de comprender y depurar. Si no tiene cuidado, el mensaje de error que recibirá probablemente le dará muy poca pista sobre cuál es realmente el problema.
Cuando Monkey Patch es un método, posiblemente descifrarás código que se basa en ese comportamiento.
Cuando agrega un nuevo método a una clase existente usando Monkey Patching, potencialmente está abriendo casos de bordes extraños que no es posible prever.
¿Cuándo está bien para Monkey Patch?
Ahora bien, dicho esto, no tiene sentido tener herramientas poderosas como Monkey Patching si no las usas realmente.
Hay casos donde la reapertura de una clase tiene sentido.
Por ejemplo, a menudo se ven Parches de mono que simplemente agregan un método de conveniencia que no tiene ningún efecto secundario. Ruby tiene una sintaxis muy hermosa, por lo que puede ser tentador para Monkey Patch convertir una clase de método feo en algo que sea más legible.
O tal vez necesites Monkey Patch, una clase que posees.
Hay muchos casos en los que está bien para Monkey Patch, pero definitivamente no debería ser tu primera arma de elección.
A menudo será el caso de que Monkey Patching es solo la preferencia del desarrollador perezosa sobre la refactorización o implementación de un patrón de diseño conocido para un problema en particular.
El hecho de que Monkey Patching le ofrezca una solución fácil no significa que deba tomar ese camino.
http://culttt.com/2015/06/17/what-is-monkey-patching-in-ruby/
En Python, el parche monopatín se refiere a mucho como un signo de vergüenza: "Tuve que enviarme un parche en esta clase porque ..." (Lo encontré primero cuando trato con Zope, que el artículo menciona). Se solía decir que era necesario tomar una clase ascendente y arreglarla en tiempo de ejecución en lugar de cabildear para corregir los comportamientos no deseados en la clase real o arreglarlos en una subclase. En mi experiencia, la gente de Ruby no habla tanto de parchear, porque no se considera especialmente malo o incluso digno de mención (de ahí el "golpe de pato"). Obviamente, debe tener cuidado al cambiar los valores de retorno de un método que se utilizará en otras dependencias, pero agregar métodos a una clase de la forma en que active_support y facets lo hacen es perfectamente seguro.
Estás en lo correcto; es cuando modificas o extiendes una clase existente en lugar de subclasificarla.