Quelle est la différence entre include et extend dans Ruby?

Je me concentre sur la métaprogrammation Ruby. Les mixin / modules parviennent toujours à me confondre.

  • include : mélange les méthodes de module spécifiées en tant que méthodes d’ instance dans la classe cible
  • extend : mélange les méthodes de module spécifiées en tant que méthodes de classe dans la classe cible

Donc, la différence majeure est-elle juste ou un dragon plus grand se cache-t-il? 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!" 

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

Si vous avez une classe Klazz et que le module Mod , y compris Mod dans Klazz des exemples d’access de Klazz aux méthodes de Mod . Ou vous pouvez étendre Klazz avec Mod donnant access à la classe Klazz aux méthodes de Mod . Mais vous pouvez également étendre un object arbitraire avec o.extend Mod . Dans ce cas, l’object individuel obtient les méthodes de Mod même si tous les autres objects de la même classe ne font pas.

extend – ajoute les méthodes et les constantes du module spécifié à la métaclasse de la cible (c’est-à-dire la classe singleton), par exemple

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

include – Par défaut, il mélange les méthodes du module spécifié en tant que méthodes d’instance dans le module / la classe cible. par exemple

  • si vous appelez la class Klazz; include Mod; end; class Klazz; include Mod; end; , maintenant toutes les instances de Klazz ont access aux méthodes de Mod (en tant que méthodes d’instance)
  • include est une méthode privée, car elle est destinée à être appelée depuis la classe / le module conteneur.

Cependant , les modules remplacent très souvent le include de include en corrigeant la méthode included . Ceci est très important dans le code Rails hérité. plus de détails de Yehuda Katz .

Plus de détails sur include , avec son comportement par défaut, en supposant que vous avez exécuté 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 aucun effet
  • Il inclut également les constantes de Mod dans Klazz, tant qu’elles ne se heurtent pas
  • Il donne access à Klazz aux variables de module de Mod, par exemple @@foo ou @@bar
  • soulève ArgumentError s’il y a des inclusions cycliques
  • Attache le module comme ancêtre immédiat de l’appelant (c.-à-d. Il ajoute Mod à Klazz.ancestors, mais Mod n’est pas ajouté à la chaîne de Klazz.superclass.superclass.superclass. Ainsi, appeler super dans Klazz # foo vérifiera Mod # foo avant vérifier la méthode foo de la super-classe de Klazz (voir RubySpec pour plus de détails).

Bien sûr, la documentation de base Ruby est toujours le meilleur endroit pour ces choses. Le projet RubySpec était également une ressource fantastique, car ils ont documenté les fonctionnalités avec précision.

  • #include RubySpec rubydoc
  • #inclus RubySpec rubydoc
  • #extend RubySpec rubydoc
  • #extended RubisSpec Rubydoc étendu
  • #extend_object RubySpec rubydoc
  • #append_features RubySpec rubydoc

C’est correct.

En coulisses, include est en fait un alias pour append_features , qui (à partir des docs):

L’implémentation par défaut de Ruby consiste à append les constantes, méthodes et variables de module de ce module à aModule si ce module n’a pas déjà été ajouté à aModule ou à l’un de ses ancêtres.

Toutes les autres réponses sont bonnes, y compris le conseil pour 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

Comme pour les cas d’utilisation:

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

Si vous étendez la classe ClassThatExtends avec le module ReusableModule, les méthodes et les constantes sont copiées . Évidemment, si vous ne faites pas attention, vous pouvez gaspiller beaucoup de mémoire en dupliquant dynamicment les définitions.

Si vous utilisez ActiveSupport :: Concern, la fonctionnalité .included () vous permet de réécrire directement la classe d’inclusion. Le module ClassMethods dans une préoccupation est étendu (copié) dans la classe d’inclusion.

Je l’ai appris avant mais je l’apprécie quand je l’utilise. Voici la différence:

Cela ne fonctionne pas mais fonctionnerait si je l’avais 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 marche:

 class UserAction extend Calculations def self.page_views(campaign) overall_profit = calculate_campaign_profit(campaign) end end 

Je voudrais aussi expliquer le mécanisme tel qu’il fonctionne. Si je n’ai pas raison, veuillez 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 objects n’ont pas de méthodes, seules les classes et les modules le font. Ainsi, lorsqu’un message reçoit a méthode, il some_method a méthode de recherche dans a classe propre, puis dans A classe, puis dans A classe liée à des modules de classe A s’il y en a (dans l’ordre inverse, les derniers gains inclus).

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

Ainsi, le chemin de recherche de méthode pour a est le suivant: a => a ‘=> modules liés à une’ classe => A.

il y a aussi une méthode prépend qui change le chemin de recherche:

a => a ‘=> modulesto préfixé A => A => module inclus à A

Désolé pour mon mauvais anglais.