Solo estoy entendiendo la metaprogramación de Ruby. Los módulos / mixin siempre logran confundirme.
- incluyen : mezclas en métodos de módulo especificados como métodos de instancia en la clase de destino
- extender : se mezcla en métodos de módulo especificados como métodos de clase en la clase de destino
Entonces, ¿la diferencia principal es solo esto o hay un dragón más grande al acecho? p.ej
module ReusableModule
def module_method
puts "Module Method: Hi there!"
end
end
class ClassThatIncludes
include ReusableModule
end
class ClassThatExtends
extend ReusableModule
end
puts "Include"
ClassThatIncludes.new.module_method # "Module Method: Hi there!"
puts "Extend"
ClassThatExtends.module_method # "Module Method: Hi there!"
Respuestas:
Lo que has dicho es correcto. Sin embargo, hay más que eso.
Si tiene una clase
Klazz
y un móduloMod
, inclusoMod
enKlazz
da instancias deKlazz
acceso aMod
los métodos de. O se puede extenderKlazz
aMod
dar la clase deKlazz
acceso a losMod
métodos 's. Pero también puede extender un objeto arbitrario cono.extend Mod
. En este caso, el objeto individual obtieneMod
los métodos aunque todos los demás objetos con la misma claseo
que no.fuente
extender : agrega los métodos y constantes del módulo especificado a la metaclase del objetivo (es decir, la clase singleton), por ejemplo
Klazz.extend(Mod)
, ahora Klazz tiene los métodos de Mod (como métodos de clase)obj.extend(Mod)
, ahora obj tiene los métodos de Mod (como métodos de instancia), pero ninguna otra instancia deobj.class
tiene esos métodos agregados.extend
es un método públicoinclude : de forma predeterminada, se mezcla en los métodos del módulo especificado como métodos de instancia en el módulo / clase de destino. p.ej
class Klazz; include Mod; end;
, ahora todas las instancias de Klazz tienen acceso a los métodos de Mod (como métodos de instancia)include
es un método privado, porque está destinado a ser llamado desde la clase / módulo contenedor.Sin embargo , los módulos a menudo anulan
include
el comportamiento de los monos parcheando elincluded
método. Esto es muy prominente en el código de Rails heredado. más detalles de Yehuda Katz .Más detalles sobre
include
, con su comportamiento predeterminado, suponiendo que haya ejecutado el siguiente código@@foo
o@@bar
super
a Klazz # foo verificará Mod # foo antes de verificar al método foo de la verdadera superclase de Klazz. Ver RubySpec para más detalles).Por supuesto, la documentación de ruby core es siempre el mejor lugar para estas cosas. El proyecto RubySpec también fue un recurso fantástico, porque documentaron la funcionalidad con precisión.
#include
RubySpec rubydoc#included
RubySpec rubydoc#extend
RubySpec rubydoc#extended
RubySpec rubydoc#extend_object
RubySpec rubydoc#append_features
RubySpec rubydocfuente
extend
puede aplicar métodos como métodos de clase o instancia, dependiendo de la utilización.Klass.extend
= métodos de clase,objekt.extend
= métodos de instancia. Siempre (erróneamente) asumí que los métodos de clase proveníanextend
, y la instancia deinclude
.Eso es correcto.
Detrás de escena, include es en realidad un alias para append_features , que (de los documentos):
fuente
Cuando ingresa
include
un módulo en una clase, los métodos del módulo se importan como métodos de instancia .Sin embargo, cuando
extend
convierte un módulo en una clase, los métodos del módulo se importan como métodos de clase .Por ejemplo, si tenemos un módulo
Module_test
definido de la siguiente manera:Ahora, para el
include
módulo. Si definimos la clase de laA
siguiente manera:La salida será:
M - in module
.Si reemplazamos la línea
include Module_test
conextend Module_test
y ejecute el código de nuevo, recibimos el siguiente error:undefined method 'func' for #<A:instance_num> (NoMethodError)
.Cambio de la llamada al método
a.func
aA.func
, la salida cambia a:M - in module
.De la ejecución del código anterior, está claro que cuando somos
include
un módulo, sus métodos se convierten en métodos de instancia y cuando somosextend
un módulo, sus métodos se convierten en métodos de clase .fuente
Todas las otras respuestas son buenas, incluida la sugerencia para explorar RubySpecs:
https://github.com/rubyspec/rubyspec/blob/master/core/module/include_spec.rb
https://github.com/rubyspec/rubyspec/blob/master/core/module/extend_object_spec.rb
En cuanto a los casos de uso:
Si incluye el módulo ReusableModule en la clase ClassThatIncludes, se hace referencia a los métodos, constantes, clases, submódulos y otras declaraciones.
Si extiende la clase ClassThatExtends con el módulo ReusableModule, los métodos y las constantes se copian . Obviamente, si no tiene cuidado, puede desperdiciar mucha memoria duplicando dinámicamente las definiciones.
Si usa ActiveSupport :: Concern, la funcionalidad .included () le permite reescribir la clase incluida directamente. El módulo ClassMethods dentro de una preocupación se extiende (copia) en la clase que lo incluye.
fuente
También me gustaría explicar el mecanismo a medida que funciona. Si no estoy en lo correcto, corrija.
Cuando usamos
include
estamos agregando un enlace de nuestra clase a un módulo que contiene algunos métodos.Los objetos no tienen métodos, solo las clases y los módulos los tienen. Entonces, cuando
a
recibe un mensajesome_method
, comienza el método de búsquedasome_method
ena
la clase eigen, luego en laA
clase y luego enA
los módulos vinculados a la clase si hay algunos (en orden inverso, las últimas victorias incluidas).Cuando usamos
extend
, estamos agregando enlaces a un módulo en la clase eigen del objeto. Entonces, si usamos A.new.extend (MyMod) estamos agregando enlaces a nuestro módulo a la clase oa'
clase de instancia de A. Y si usamos A.extend (MyMod) estamos agregando un enlace a la clase propia A (objeto, las clases también son objetos)A'
.entonces la ruta de búsqueda del método
a
es la siguiente: a => a '=> módulos vinculados a una' clase => A.También hay un método de anteponer que cambia la ruta de búsqueda:
a => a '=> módulos anexados a A => A => módulo incluido a A
Perdón por mi mal ingles.
fuente