2008-10-01 13 views
344

Solo me estoy metiendo en la metaprogramación de Ruby. Los mixin/modules siempre logran confundirme.¿Cuál es la diferencia entre incluir y extender en Ruby?

  • incluyen: mezclas en los métodos de módulo especificado como métodos de instancia en la clase de objetivos
  • extienden: mezclas en los métodos de módulo especificado como métodos de clase en la clase de objetivos

Entonces, ¿la mayor diferencia es esta o está acechando un dragón más grande? p.

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

Consulte este enlace también: http://juixe.com/techknow/index.php/2006/06/15/mixins-in-ruby/ – Donato

Respuesta

209

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

Si tiene un módulo de clase Klazz y Mod, incluyendo Mod en Klazz da ejemplos de Klazz acceso a los métodos Mod 's. O se puede extender Klazz con Mod dando la clase Klazz acceso a los métodos Mod 's. Pero también puedes extender un objeto arbitrario con o.extend Mod. En este caso, el objeto individual obtiene los métodos Mod, aunque no todos los demás objetos con la misma clase que o.

13

Correcto.

Detrás de las escenas, incluir es en realidad un alias para append_features, que (de los documentos):

implementación por defecto de Ruby es añadir las constantes, métodos, y el módulo variables de este módulo a aModule si este módulo no se ha agregado a un Módulo o uno de sus antepasados.

274

extender - añade métodos y constantes del módulo especificado a metaclase del objetivo (es decir, la clase singleton) por ejemplo

  • si llama Klazz.extend(Mod), ahora Klazz tiene métodos de MOD (como los métodos de clase)
  • Si llama obj.extend(Mod), ahora obj tiene métodos de la MOD (como métodos de instancia), pero ninguna otra instancia de de obj.class tiene esos métodos adicional.
  • extend es un método público

incluyen - Por defecto, se mezcla en los métodos del módulo especificado como métodos de instancia en el módulo de destino/clase. p.

  • si llama class Klazz; include Mod; end;, ahora todas las instancias de Klazz tienen acceso a los métodos de la MOD (como métodos de instancia)
  • include es un método privado, ya que está diseñada para ser llamado desde dentro de la clase de contenedor/módulo.

Sin embargo, módulos muy a menudo overrideinclude 's comportamiento por mono-parchear el método included. Esto es muy prominente en el código de Rails heredado. more details from Yehuda Katz.

Más detalles sobre include, con su comportamiento predeterminado, suponiendo que se le han acabado el siguiente código

class Klazz 
    include Mod 
end 
  • Si Mod ya está incluido en Klazz, o uno de sus antepasados, el incluir la afirmación no tiene efecto
  • También incluye las constantes de Mod en Klazz, siempre que no entren en conflicto
  • Da acceso a Klazz a las variables del módulo de Mod, por ejemplo @@foo o @@bar
  • plantea ArgumentError si hay cíclico incluye
  • Se conecta el módulo como antepasado inmediato de la persona que llama (es decir, se añade Mod a Klazz.ancestors, pero Mod no se añade a la cadena de Klazz.superclass.superclass.superclass Así que, llamando al super en Klazz # foo verificará Mod # foo antes de verificar el método foo de la verdadera superclase de Klazz. Consulte RubySpec para obtener más detalles).

Por supuesto, the ruby core documentation es siempre el mejor lugar para ir a estas cosas. The RubySpec project también fue un recurso fantástico, porque documentaron la funcionalidad con precisión.

+13

Sé que esta es una publicación bastante antigua, pero la claridad de la respuesta no pude detener mi comentario. Muchas gracias por una buena explicación. – MohamedSanaulla

+0

[RubySpec] (http://www.rubyspec.org/) murió :( – wiseland

+0

@systho Ese enlace no funciona ahora – Anwar

4

Todas las otras respuestas son buenas, incluyendo la punta de excavar a través 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 usted incluye módulo ReusableModule en la clase ClassThatIncludes, se hace referencia a los métodos, constantes, clases, submódulos y otras declaraciones.

Si extiendes ClassThatExtends clase con ReusableModule módulo, a continuación, los métodos y las constantes obtiene copiado. 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 que incluye directamente. El módulo ClassMethods dentro de una Concern obtiene extendido (copiado) en la clase incluida.

1

Lo aprendí antes pero lo aprecio cuando lo uso. Aquí está la diferencia:

Esto no funciona, pero funcionaría si 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 
1

También me gustaría explicar el mecanismo como funciona. Si no estoy bien, corrígelo.

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 lo hacen. Entonces cuando a recibe el mensaje some_method comienza el método de búsqueda de la clase propia, luego en la clase A y luego en los módulos de clase A si hay algunos (en orden inverso, último incluido, gana).

Cuando usamos extend estamos agregando enlaces a un módulo en la clase propia del objeto. Así que si usamos A.new.extend (MyMod) estamos agregando enlaces a nuestro módulo a la clase de instancia E oa 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'.

tan ruta de búsqueda de métodos para a es como sigue: a => a '=> módulos vinculados a una' clase => A.

también hay un método prepend que cambia el camino de búsqueda:

a => a '=> módulos añadidos a A => A => módulo incluido a A

lo siento por mi mal inglés.

Cuestiones relacionadas