Pregunta

Solo estoy entendiendo la metaprogramación de Ruby. Los módulos / mixin siempre logran confundirme.

  • include : se mezcla en métodos de módulo específicos 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, ¿es la principal diferencia solo esto o hay un dragón más grande 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!"
¿Fue útil?

Solución

Lo que has dicho es correcto. Sin embargo, hay más que eso.

Si tiene una clase Klazz y el módulo Mod , incluyendo Mod en Klazz proporciona instancias de Klazz accede a los métodos de Mod . O puede extender Klazz con Mod dando a la clase Klazz acceso a Mod ' s métodos. 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 .

Otros consejos

extender : agrega los métodos y constantes del módulo especificado a la metaclase del objetivo (es decir, la clase Singleton)   p.ej.

  • si llama a Klazz.extend (Mod) , ahora Klazz tiene los 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 de obj.class tiene esos métodos añadido.
  • extend es un método público

include : de forma predeterminada, combina los métodos del módulo especificado como métodos de instancia en el módulo / clase de destino.   por ejemplo,

  • si llamas a clase Klazz; incluir 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 anular incluyen el comportamiento de mediante la aplicación de parches al método incluido . 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

class Klazz
  include Mod
end
  • Si Mod ya está incluido en Klazz, o uno de sus antepasados, la declaración de inclusión no tiene efecto
  • También incluye las constantes de Mod en Klazz, siempre que no choquen
  • Le da a Klazz acceso a las variables del módulo de Mod, p. @@ foo o @@bar
  • plantea ArgumentError si hay inclusiones cíclicas
  • Conecta el módulo como el antepasado inmediato del llamante (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 buscará Mod # foo antes de verificar el método foo de superclase real de Klazz. Consulte RubySpec para obtener más detalles.).

Por supuesto, la documentación de Ruby Core siempre es 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.

Eso es correcto.

Detrás de escena, include es en realidad un alias para append_features , que (de los documentos):

  

La implementación por defecto de Ruby es   agregar las constantes, métodos y módulo   variables 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 antepasados.

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 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, entonces los métodos y las constantes se copian . Obviamente, si no tiene cuidado, puede perder 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 obtiene extendido (copiado) en la clase que incluye.

También me gustaría explicar el mecanismo, ya 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.

class A
include MyMOd
end

a = A.new
a.some_method

Los objetos no tienen métodos, solo clases y módulos. Por lo tanto, cuando a recibe el mensaje some_method , comienza el método de búsqueda some_method en la clase eigen de a , luego en A clase y luego en vinculados a los módulos de clase A si hay algunos (en orden inverso, el último incluye victorias).

Cuando usamos extender 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 eigen de instancia de A o la clase a '. Y si usamos A.extend (MyMod) estamos agregando enlaces a A (los objetos, las clases también son objetos) eigenclass A '.

entonces la ruta de búsqueda de método para 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 inglés.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top