module - ¿Cuál es la diferencia entre incluir y extender en Ruby?




include (6)

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

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!"

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 de obj.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.


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.


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 .


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.


Aquí hay una buena explicación:

[La] respuesta simple es que requieren e incluyen esencialmente no están relacionados.

"Requerir" es similar a la inclusión de C, lo que puede causar confusión en el novato (Una diferencia notable es que los locales dentro del archivo requerido se "evaporan" cuando se realiza el requerimiento).

La inclusión de Ruby no es nada como la inclusión de C. La declaración de inclusión "mezcla en" un módulo en una clase. Es una forma limitada de herencia múltiple . Un módulo incluido otorga literalmente una relación "is-a" a la cosa que lo incluye.

Énfasis añadido.





ruby module include extend