¿Cuál es la diferencia entre incluir y extender en Ruby?
Estoy pensando en la metaprogramación de Ruby. Los mixin / módulos siempre logran confundirme.
- include : mezcla en métodos de módulo especificados como métodos de instancia en la clase de destino
- extend : mezcla en métodos de módulo especificados como métodos de clase en la clase de destino
Entonces, ¿la gran diferencia es solo esto o un dragón más grande está al acecho? por ejemplo,
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!"
6 answers
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
da instancias de Klazz
acceso a los métodos de Mod
. O puedes extender Klazz
con Mod
dando 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 todos los demás objetos con la misma clase que o
no lo hacen.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-07 02:32:47
Extend - agrega los métodos y constantes del módulo especificado a la metaclase del objetivo (es decir, la clase singleton) por ejemplo,
- si llamas a
Klazz.extend(Mod)
, ahora Klazz tiene los métodos de Mod (como métodos de clase) - si llamas a
obj.extend(Mod)
, ahora obj tiene los métodos de Mod (como métodos de instancia), pero ninguna otra instancia de ofobj.class
tiene esos métodos agregados. -
extend
es un método público
Include - Por defecto, se mezcla en el módulo especificado métodos como métodos de instancia en el módulo/clase de destino. por ejemplo,
- si llama a
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 dentro de la clase/módulo contenedor.
Sin embargo , los módulos muy a menudo reemplazan include
's comportamiento por mono-parcheando el método included
. Esto es muy prominente en el código 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
class Klazz
include Mod
end
- Si Mod ya está incluido en Klazz, o uno de sus antepasados, la instrucción include no tiene efecto
- También incluye constantes de Mod en Klazz, siempre y cuando no choquen
- Le da acceso a Klazz a las variables de módulo de Mod, por ejemplo,
@@foo
o@@bar
- plantea ArgumentError si hay cíclico incluye
- Adjunta el módulo como antepasado inmediato del llamante (es decir, añade Mod a Klazz.antepasados, pero Mod no se añade a la cadena de Klazz.superclase.superclase.superclase. Por lo tanto, llamando a
super
en Klazz#foo comprobará el Mod#foo antes de comprobar el método foo de la superclase real de Klazz. Vea el RubySpec para más detalles.).
Por supuesto, la documentación de ruby core es siempre el mejor lugar para ir para estas cosas. El proyecto RubySpec también fue un fantástico recurso, porque documentaron la funcionalidad con precisión.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-22 21:53:23
Eso es correcto.
Detrás de las escenas, include es en realidad un alias para append_features , que (de los documentos):
La implementación predeterminada de Ruby es agregar las constantes, métodos y módulo variables de este módulo a aModule si este módulo aún no ha sido añadido a aModule o a uno de sus antepasados.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-01 08:08:30
Todas las otras respuestas son buenas, incluyendo la punta para cavar a través de 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 module ReusableModule en la clase ClassThatIncludes, se hace referencia a los métodos, constantes, clases, submódulos y otras declaraciones.
Si extiendes class ClassThatExtends with module ReusableModule, entonces los métodos y constantes se copian . Obviamente, si no tiene cuidado, puede desperdiciar mucha memoria duplicando dinámicamente las definiciones.
Si utiliza ActiveSupport:: Concern, el .la funcionalidad included () le permite reescribir la clase de inclusión directamente. module ClassMethods inside a Concern gets extended (copied) into the including class.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-22 21:02:06
Lo aprendí antes, pero lo aprecio cuando lo uso. Aquí está la diferencia:
Esto no funciona, pero funcionaría si lo he 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-02 12:21:13
También me gustaría explicar el mecanismo como funciona. Si no tengo razón, 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 sí.
Así que cuando a
recibe mesage some_method
comienza el método de búsqueda some_method
en la clase propia de a
, luego en la clase A
y luego en los módulos de clase vinculados a A
si hay algunos (en orden inverso, último incluido ganar).
Cuando usamos extend
estamos agregando enlace a un módulo en la clase eigen del objeto.
Así que si usamos A. nuevo.extend (MyMod) estamos agregando un enlace a nuestro módulo a la clase eigen de instancia de A o a la clase a'
.
Y si usamos A. extend (MyMod) estamos agregando un enlace a A(object's, las clases también son objetos) eigenclass A'
.
Así que la ruta de búsqueda del método para a
es la siguiente:
a = > a' = > módulos vinculados a a ' class = > A.
También hay un método de anteponer que cambia la búsqueda ruta:
A => a' = > módulos antepuestos A = > A = > módulo incluido a
Lo siento por mi mal inglés.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-27 12:11:02