2008-10-01 21 views
344

Juste en train de faire la métaprogrammation de Ruby. Les mixin/modules parviennent toujours à me confondre.Quelle est la différence entre include et extend dans Ruby?

  • comprennent: Les mélanges dans les méthodes de module spécifié comme méthodes d'instance dans la classe cible
  • étendent: Les mélanges dans des procédés de module spécifié comme méthodes de classe dans la classe cible

Est-ce que la différence majeure est juste ceci ou est-ce qu'un plus grand dragon se cache? par exemple.

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

Cochez également ce lien: http://juixe.com/techknow/index.php/2006/06/15/mixins-in-ruby/ – Donato

Répondre

209

Ce que vous avez dit est correct. Cependant, il y a plus que cela.

Si vous avez une classe Klazz module Mod, y compris dans ModKlazz donne des cas de Klazz accès aux méthodes de Mod. Ou vous pouvez étendre Klazz avec Mod en donnant la classeKlazz accès aux méthodes de Mod. Mais vous pouvez également étendre un objet arbitraire avec o.extend Mod. Dans ce cas, l'objet individuel obtient les méthodes de Mod même si tous les autres objets de la même classe que o ne le font pas.

13

C'est correct.

Dans les coulisses, notamment est en fait un alias pour append_features, qui (à partir de la documentation):

implémentation par défaut de Ruby est ajouter les constantes, les méthodes et le module variables de ce module à un module si ce module n'a pas encore été ajouté à unModule ou à l'un de ses ancêtres.

274

étendre - ajoute les méthodes et les constantes du module spécifié à la métaclasse de la cible (c'est-à-dire la classe singleton) - par ex.

  • si vous appelez Klazz.extend(Mod), maintenant Klazz a des méthodes de Mod (comme les méthodes de classe)
  • si vous appelez obj.extend(Mod), maintenant obj a des méthodes de Mod (comme les méthodes d'instance), mais aucune autre instance de de obj.class a ces méthodes ajoutée.
  • extend est une méthode publique

comprennent - Par défaut, il se mélange dans les méthodes de module spécifié comme méthodes d'instance dans le module cible/classe. par exemple.

  • si vous appelez class Klazz; include Mod; end;, maintenant toutes les instances de Klazz ont accès aux méthodes de Mod (comme les méthodes d'instance)
  • include est une méthode privée, car il est destiné à être appelé à partir de la classe conteneur/module.

Cependant, modules très souvent override comportement de include par singe rapiéçage la méthode included. Ceci est très important dans le code Rails hérité. more details from Yehuda Katz.

De plus amples détails sur include, avec son comportement par défaut, en supposant que vous avez exécutez le code suivant

class Klazz 
    include Mod 
end 
  • Si Mod est déjà inclus dans Klazz, ou l'un de ses ancêtres, l'instruction include n'a pas effet
  • Il inclut également les constantes de Mod dans Klazz, à condition qu'elles ne s'affichent pas
  • Il donne à Klazz un accès aux variables du module de Mod, par ex. @@foo ou @@bar
  • soulève ArgumentError s'il y a cyclique comprend
  • le module comme attache ancêtre immédiat de l'appelant (il ajoute Mod à Klazz.ancestors, mais Mod est pas ajouté à la chaîne de Klazz.superclass.superclass.superclass Donc, en appelant super dans Klazz # foo, vous allez vérifier le Mod # foo avant de vérifier la méthode foo de la superclasse de Klazz (voir RubySpec pour plus de détails).

Bien sûr, the ruby core documentation est toujours le meilleur endroit où aller pour ces choses. The RubySpec project était aussi une ressource fantastique, car ils documentaient précisément la fonctionnalité.

+13

Je sais que c'est un post assez ancien, mais la clarté de la réponse ne pouvait pas me retenir de commenter. Merci beaucoup pour une bonne explication. – MohamedSanaulla

+0

[RubySpec] (http://www.rubyspec.org/) est mort :( – wiseland

+0

@systho Ce lien ne fonctionne pas maintenant – Anwar

4

Toutes les autres réponses sont bonnes, y compris la pointe de creuser à travers 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 ce qui concerne les cas d'utilisation:

Si vous inclure module ReusableModule dans la classe ClassThatIncludes, les méthodes, constantes, classes, sous-modules et autres déclarations sont référencés.

Si vous prolongez ClassThatExtends de classe avec module ReusableModule, puis les méthodes et les constantes obtient copié. Évidemment, si vous ne faites pas attention, vous pouvez perdre beaucoup de mémoire en dupliquant dynamiquement les définitions.

Si vous utilisez ActiveSupport :: Concern, la fonctionnalité .included() vous permet de réécrire la classe incluse directement. module ClassMethods à l'intérieur d'un problème obtient étendu (copié) dans la classe inclusive.

1

Je l'ai déjà appris mais je l'apprécie quand je l'utilise. Voici la différence:

Cela ne fonctionne pas, mais cela fonctionnerait si je l'ai défini comme def page_views(campaign):

class UserAction 
    include Calculations 

    def self.page_views(campaign) 
    overall_profit = calculate_campaign_profit(campaign) 
    end 
end 

Cela fonctionne:

class UserAction 
    extend Calculations 

    def self.page_views(campaign) 
    overall_profit = calculate_campaign_profit(campaign) 
    end 
end 
1

Je voudrais aussi expliquer la mécanisme tel qu'il fonctionne. Si je ne suis pas correct s'il vous plaît corriger.

Lorsque nous utilisons include nous ajoutons un lien de notre classe à un module qui contient certaines méthodes.

class A 
include MyMOd 
end 

a = A.new 
a.some_method 

Les objets n'ont pas de méthodes, seuls les clases et les modules le font. Lorsque a reçoit le mesage some_method il commence la méthode de recherche some_method dans la classe propre a, puis dans la classe A puis dans les modules de classe A s'il y en a (dans l'ordre inverse, les derniers inclus).

Lorsque nous utilisons extend, nous ajoutons une liaison à un module dans la classe propre de l'objet. Donc, si nous utilisons A.new.extend (MyMod), nous ajoutons un lien vers notre module à la classe propre de l'instance A ou à la classe a'. Et si nous utilisons A.extend (MyMod) nous ajoutons un lien vers A (les objets, les classes sont aussi des objets) eigenclass A'.

donc procédé chemin de recherche pour a est la suivante: a => a '=> modules reliés à un' class => A.

également il existe un procédé de préfixe qui change chemin de recherche:

a => a '=> module ajouté A => A => module inclus à A

désolé pour mon mauvais anglais.