¿Cuál es la diferencia entre incluir y extender en Ruby?
module include (6)
Solo metiendo la cabeza en Ruby metaprogramando. Los mixin / módulos siempre logran confundirme.
- incluye : mezclas en métodos de módulo especificados como métodos de instancia en la clase objetivo
- extend : mezcla en métodos de módulo especificados como métodos de clase en la clase objetivo
Entonces, ¿la principal diferencia es solo esto o acecha un dragón más grande? 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!"
Eso es correcto.
Detrás de escena, include es en realidad un alias para append_features , que (de la documentación):
La implementación predeterminada de Ruby es agregar las constantes, métodos y variables de módulo de este módulo a un Módulo si este módulo aún no se ha agregado a un Módulo o uno de sus ancestros.
Lo aprendí antes, pero lo aprecio cuando lo uso. Aquí está la diferencia:
Esto no funciona pero funcionaría si lo hubiera definido como def page_views(campaign)
:
class UserAction
include Calculations
def self.page_views(campaign)
overall_profit = calculate_campaign_profit(campaign)
end
end
Esto funciona:
class UserAction
extend Calculations
def self.page_views(campaign)
overall_profit = calculate_campaign_profit(campaign)
end
end
Lo que has dicho es correcto. Sin embargo, hay más que eso.
Si tiene una clase Klazz
y un módulo Mod
, incluyendo Mod
en Klazz
le da instancias de acceso Klazz
a los métodos de Mod
. O puede extender Klazz
con Mod
dando a la clase Klazz
acceso a los métodos de Mod
. Pero también puede extender un objeto arbitrario con o.extend Mod
. En este caso, el objeto individual obtiene los métodos de Mod
, aunque no todos los demás objetos con la misma clase que o
.
También me gustaría explicar el mecanismo como funciona. Si no estoy en lo correcto, por favor corrija.
Cuando usamos include
estamos agregando un enlace de nuestra clase a un módulo que contiene algunos métodos.
class A
include MyMOd
end
a = A.new
a.some_method
Los objetos no tienen métodos, solo las clases y los módulos. Entonces, cuando a
recibe a
mensaje some_method
, comienza a buscar el método some_method
en la clase de eigen de a, luego en la clase A
y luego en los módulos de clase A
vinculados si hay algunos (en orden inverso, las últimas victorias incluidas).
Cuando usamos la extend
, estamos agregando un enlace a un módulo en la clase eigen del objeto. Entonces, si usamos A.new.extend (MyMod) estamos agregando un enlace a nuestro módulo a la clase eigen de instancia de A o a a''
clase a''
. Y si usamos A.extend (MyMod) estamos agregando enlaces a A (objeto, las clases también son objetos) eigenclass A''
.
por lo tanto, la ruta de búsqueda de métodos para a
es la siguiente: a => a ''=> módulos vinculados a'' class => A.
También hay un método prepend que cambia la ruta de búsqueda:
a => a ''=> modulesto prependido A => A => módulo incluido a A
Perdón por mi mal ingles.
Todas las demás respuestas son buenas, incluida la sugerencia para excavar en 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 de inclusión directamente. El módulo ClassMethods dentro de una preocupación se extiende (copia) a la clase que incluye.
extender : agrega los métodos y constantes del módulo especificado a la metaclase del objetivo (es decir, la clase singleton), por ejemplo
- si llama a
Klazz.extend(Mod)
, ahora Klazz tiene métodos de Mod (como métodos de clase) - si llama a
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úblico
include - 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
- si llamas
class Klazz; include Mod; end;
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á diseñado para ser llamado desde la clase / módulo contenedor.
Sin embargo , los módulos muy a menudo anulan el comportamiento de include
mediante el parche de mono del método included
. Esto es muy importante en el código de Rails heredado. más detalles de Yehuda Katz .
Más detalles sobre include
, con su comportamiento predeterminado, asumiendo que ha ejecutado el siguiente código
class Klazz
include Mod
end
- Si Mod ya está incluido en Klazz, o uno de sus ancestros, la declaración de inclusión no tiene efecto
- También incluye las constantes de Mod en Klazz, siempre y cuando no entren en conflicto.
- Le da a Klazz acceso a las variables del módulo de Mod, por ejemplo,
@@foo
o@@bar
- plantea ArgumentError si hay inclusiones cíclicas
- Agrega el módulo como el antepasado inmediato de la persona que llama (es decir, agrega Mod a Klazz.ancestors, pero Mod no se agrega a la cadena de Klazz.superclass.superclass.superclass. Por lo tanto, llamar a
super
en Klazz # foo verificará Mod # foo antes comprobando el método foo de la superclase real de Klazz. Consulte RubySpec para obtener más información.
Por supuesto, la documentación de ruby core es siempre el mejor lugar para ir por estas cosas. El proyecto RubySpec también fue un recurso fantástico, ya que documentaron la funcionalidad con precisión.